Remove Utility module and move its content to Core and TextRenderer modules

This commit is contained in:
SirLynix
2024-02-10 22:46:53 +01:00
committed by Jérôme Leclercq
parent 965a00182c
commit e64c2b036e
364 changed files with 2336 additions and 2516 deletions

View File

@@ -0,0 +1,66 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TEXTRENDERER_ABSTRACTTEXTDRAWER_HPP
#define NAZARA_TEXTRENDERER_ABSTRACTTEXTDRAWER_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Core/Color.hpp>
#include <Nazara/Math/Rect.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/TextRenderer/Config.hpp>
#include <memory>
namespace Nz
{
class AbstractImage;
class Font;
class NAZARA_TEXTRENDERER_API AbstractTextDrawer
{
public:
struct Glyph;
struct Line;
AbstractTextDrawer() = default;
virtual ~AbstractTextDrawer();
virtual void Clear() = 0;
virtual const Rectf& GetBounds() const = 0;
virtual const std::shared_ptr<Font>& GetFont(std::size_t index) const = 0;
virtual std::size_t GetFontCount() const = 0;
virtual const Glyph& GetGlyph(std::size_t index) const = 0;
virtual std::size_t GetGlyphCount() const = 0;
virtual const Line& GetLine(std::size_t index) const = 0;
virtual std::size_t GetLineCount() const = 0;
inline std::size_t GetLineGlyphCount(std::size_t index) const;
virtual float GetMaxLineWidth() const = 0;
virtual void SetMaxLineWidth(float lineWidth) = 0;
struct Glyph
{
Color color;
Rectf bounds;
Rectui atlasRect;
Vector2f corners[4];
AbstractImage* atlas;
bool flipped;
int renderOrder;
};
struct Line
{
Rectf bounds;
std::size_t glyphIndex;
};
};
}
#include <Nazara/TextRenderer/AbstractTextDrawer.inl>
#endif // NAZARA_TEXTRENDERER_ABSTRACTTEXTDRAWER_HPP

View File

@@ -0,0 +1,22 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/TextRenderer/Debug.hpp>
namespace Nz
{
inline std::size_t AbstractTextDrawer::GetLineGlyphCount(std::size_t index) const
{
std::size_t lineCount = GetLineCount();
const auto& lineInfo = GetLine(index);
if (index == lineCount - 1)
return GetGlyphCount() - lineInfo.glyphIndex;
const auto& nextLineInfo = GetLine(index + 1);
return nextLineInfo.glyphIndex - lineInfo.glyphIndex;
}
}
#include <Nazara/TextRenderer/DebugOff.hpp>

View File

@@ -0,0 +1,45 @@
/*
Nazara Engine - Platform module
Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#ifndef NAZARA_TEXTRENDERER_CONFIG_HPP
#define NAZARA_TEXTRENDERER_CONFIG_HPP
/// Each modification of a parameter needs a recompilation of the module
// Activate the security tests based on the code (Advised for development)
#define NAZARA_TEXTRENDERER_SAFE 1
#if defined(NAZARA_STATIC)
#define NAZARA_TEXTRENDERER_API
#else
#ifdef NAZARA_TEXTRENDERER_BUILD
#define NAZARA_TEXTRENDERER_API NAZARA_EXPORT
#else
#define NAZARA_TEXTRENDERER_API NAZARA_IMPORT
#endif
#endif
#endif // NAZARA_TEXTRENDERER_CONFIG_HPP

View File

@@ -0,0 +1,17 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TEXTRENDERER_CONFIGCHECK_HPP
#define NAZARA_TEXTRENDERER_CONFIGCHECK_HPP
/// This file is used to check the constant values defined in Config.hpp
#include <type_traits>
#define NazaraCheckTypeAndVal(name, type, op, val, err) static_assert(std::is_ ##type <decltype(name)>::value && name op val, #type err)
#undef NazaraCheckTypeAndVal
#endif // NAZARA_TEXTRENDERER_CONFIGCHECK_HPP

View File

@@ -0,0 +1,5 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
// no header guards

View File

@@ -0,0 +1,5 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
// no header guards

View File

@@ -0,0 +1,160 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
///TODO: FontManager ?
#pragma once
#ifndef NAZARA_TEXTRENDERER_FONT_HPP
#define NAZARA_TEXTRENDERER_FONT_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Core/AbstractAtlas.hpp>
#include <Nazara/Core/Enums.hpp>
#include <Nazara/Core/ObjectLibrary.hpp>
#include <Nazara/Core/Resource.hpp>
#include <Nazara/Core/ResourceLoader.hpp>
#include <Nazara/Core/ResourceParameters.hpp>
#include <Nazara/TextRenderer/Config.hpp>
#include <memory>
#include <unordered_map>
namespace Nz
{
struct NAZARA_TEXTRENDERER_API FontParams : ResourceParameters
{
bool IsValid() const;
};
class Font;
class FontData;
struct FontGlyph;
using FontLibrary = ObjectLibrary<Font>;
using FontLoader = ResourceLoader<Font, FontParams>;
class NAZARA_TEXTRENDERER_API Font : public Resource
{
friend FontLibrary;
friend FontLoader;
friend class TextRenderer;
public:
struct Glyph;
struct SizeInfo;
using Params = FontParams;
Font();
Font(const Font&) = delete;
Font(Font&&) = delete;
~Font();
void ClearGlyphCache();
void ClearKerningCache();
void ClearSizeInfoCache();
bool Create(std::unique_ptr<FontData> data);
void Destroy();
bool ExtractGlyph(unsigned int characterSize, char32_t character, TextStyleFlags style, float outlineThickness, FontGlyph* glyph) const;
const std::shared_ptr<AbstractAtlas>& GetAtlas() const;
std::size_t GetCachedGlyphCount(unsigned int characterSize, TextStyleFlags style, float outlineThickness) const;
std::size_t GetCachedGlyphCount() const;
std::string GetFamilyName() const;
int GetKerning(unsigned int characterSize, char32_t first, char32_t second) const;
const Glyph& GetGlyph(unsigned int characterSize, TextStyleFlags style, float outlineThickness, char32_t character) const;
unsigned int GetGlyphBorder() const;
unsigned int GetMinimumStepSize() const;
const SizeInfo& GetSizeInfo(unsigned int characterSize) const;
std::string GetStyleName() const;
bool IsValid() const;
bool Precache(unsigned int characterSize, TextStyleFlags style, float outlineThickness, char32_t character) const;
bool Precache(unsigned int characterSize, TextStyleFlags style, float outlineThickness, std::string_view characterSet) const;
void SetAtlas(std::shared_ptr<AbstractAtlas> atlas);
void SetGlyphBorder(unsigned int borderSize);
void SetMinimumStepSize(unsigned int minimumStepSize);
Font& operator=(const Font&) = delete;
Font& operator=(Font&&) = delete;
static std::shared_ptr<AbstractAtlas> GetDefaultAtlas();
static const std::shared_ptr<Font>& GetDefault();
static unsigned int GetDefaultGlyphBorder();
static unsigned int GetDefaultMinimumStepSize();
static std::shared_ptr<Font> OpenFromFile(const std::filesystem::path& filePath, const FontParams& params = FontParams());
static std::shared_ptr<Font> OpenFromMemory(const void* data, std::size_t size, const FontParams& params = FontParams());
static std::shared_ptr<Font> OpenFromStream(Stream& stream, const FontParams& params = FontParams());
static void SetDefaultAtlas(std::shared_ptr<AbstractAtlas> atlas);
static void SetDefaultGlyphBorder(unsigned int borderSize);
static void SetDefaultMinimumStepSize(unsigned int minimumStepSize);
struct Glyph
{
std::size_t layerIndex;
Recti aabb;
Rectui atlasRect;
bool requireFauxBold;
bool requireFauxItalic;
bool flipped;
bool valid;
float fauxOutlineThickness;
int advance;
};
struct SizeInfo
{
int spaceAdvance;
unsigned int lineHeight;
float underlinePosition;
float underlineThickness;
};
// Signals:
NazaraSignal(OnFontAtlasChanged, const Font* /*font*/);
NazaraSignal(OnFontAtlasLayerChanged, const Font* /*font*/, AbstractImage* /*oldLayer*/, AbstractImage* /*newLayer*/);
NazaraSignal(OnFontDestroy, const Font* /*font*/);
NazaraSignal(OnFontGlyphCacheCleared, const Font* /*font*/);
NazaraSignal(OnFontKerningCacheCleared, const Font* /*font*/);
NazaraSignal(OnFontRelease, const Font* /*font*/);
NazaraSignal(OnFontSizeInfoCacheCleared, const Font* /*font*/);
private:
using GlyphMap = std::unordered_map<char32_t, Glyph>;
UInt64 ComputeKey(unsigned int characterSize, TextStyleFlags style, float outlineThickness) const;
void OnAtlasCleared(const AbstractAtlas* atlas);
void OnAtlasLayerChange(const AbstractAtlas* atlas, AbstractImage* oldLayer, AbstractImage* newLayer);
const Glyph& PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, TextStyleFlags style, float outlineThickness, char32_t character) const;
static bool Initialize();
static void Uninitialize();
NazaraSlot(AbstractAtlas, OnAtlasCleared, m_atlasClearedSlot);
NazaraSlot(AbstractAtlas, OnAtlasLayerChange, m_atlasLayerChangeSlot);
std::shared_ptr<AbstractAtlas> m_atlas;
std::unique_ptr<FontData> m_data;
mutable std::unordered_map<UInt64, std::unordered_map<UInt64, int>> m_kerningCache;
mutable std::unordered_map<UInt64, GlyphMap> m_glyphes;
mutable std::unordered_map<UInt64, SizeInfo> m_sizeInfoCache;
unsigned int m_glyphBorder;
unsigned int m_minimumStepSize;
static std::shared_ptr<AbstractAtlas> s_defaultAtlas;
static std::shared_ptr<Font> s_defaultFont;
static unsigned int s_defaultGlyphBorder;
static unsigned int s_defaultMinimumStepSize;
};
}
#include <Nazara/TextRenderer/Font.inl>
#endif // NAZARA_TEXTRENDERER_FONT_HPP

View File

@@ -0,0 +1,12 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <memory>
#include <Nazara/TextRenderer/Debug.hpp>
namespace Nz
{
}
#include <Nazara/TextRenderer/DebugOff.hpp>

View File

@@ -0,0 +1,44 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TEXTRENDERER_FONTDATA_HPP
#define NAZARA_TEXTRENDERER_FONTDATA_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Core/Enums.hpp>
#include <Nazara/TextRenderer/Config.hpp>
#include <string>
namespace Nz
{
struct FontGlyph;
class NAZARA_TEXTRENDERER_API FontData
{
public:
FontData() = default;
virtual ~FontData();
virtual bool ExtractGlyph(unsigned int characterSize, char32_t character, TextStyleFlags style, float outlineThickness, FontGlyph* dst) = 0;
virtual std::string GetFamilyName() const = 0;
virtual std::string GetStyleName() const = 0;
virtual bool HasKerning() const = 0;
virtual bool IsScalable() const = 0;
virtual int QueryKerning(unsigned int characterSize, char32_t first, char32_t second) const = 0;
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 SupportsOutline(float outlineThickness) const = 0;
virtual bool SupportsStyle(TextStyleFlags style) const = 0;
};
}
#endif // NAZARA_TEXTRENDERER_FONTDATA_HPP

View File

@@ -0,0 +1,22 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TEXTRENDERER_FONTGLYPH_HPP
#define NAZARA_TEXTRENDERER_FONTGLYPH_HPP
#include <Nazara/Core/Image.hpp>
namespace Nz
{
struct FontGlyph
{
Image image;
Recti aabb;
int advance;
};
}
#endif // NAZARA_TEXTRENDERER_FONTGLYPH_HPP

View File

@@ -0,0 +1,80 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TEXTRENDERER_RICHTEXTBUILDER_HPP
#define NAZARA_TEXTRENDERER_RICHTEXTBUILDER_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Core/Enums.hpp>
#include <Nazara/TextRenderer/Font.hpp>
#include <memory>
#include <string>
namespace Nz
{
class Font;
template<typename T>
class RichTextBuilder
{
public:
struct CharacterSizeWrapper;
struct CharacterSpacingOffsetWrapper;
struct LineSpacingOffsetWrapper;
struct OutlineColorWrapper;
struct OutlineThicknessWrapper;
RichTextBuilder(T& richText);
RichTextBuilder(T* richText);
RichTextBuilder(const RichTextBuilder&) = delete;
RichTextBuilder(RichTextBuilder&&) = delete;
~RichTextBuilder() = default;
inline RichTextBuilder& AppendText(std::string_view text);
inline RichTextBuilder& SetCharacterSize(unsigned int characterSize);
inline RichTextBuilder& SetCharacterSpacingOffset(float offset);
inline RichTextBuilder& SetLineSpacingOffset(float offset);
inline RichTextBuilder& SetTextColor(const Color& color);
inline RichTextBuilder& SetTextFont(const std::shared_ptr<Font>& font);
inline RichTextBuilder& SetTextOutlineColor(const Color& color);
inline RichTextBuilder& SetTextOutlineThickness(float thickness);
inline RichTextBuilder& SetTextStyle(TextStyleFlags style);
RichTextBuilder& operator=(const RichTextBuilder&) = delete;
RichTextBuilder& operator=(RichTextBuilder&&) = delete;
inline RichTextBuilder& operator<<(const Color& textColor);
inline RichTextBuilder& operator<<(const std::shared_ptr<Font>& font);
inline RichTextBuilder& operator<<(std::string_view str);
inline RichTextBuilder& operator<<(CharacterSizeWrapper characterSize);
inline RichTextBuilder& operator<<(CharacterSpacingOffsetWrapper characterSpacingOffset);
inline RichTextBuilder& operator<<(LineSpacingOffsetWrapper lineSpacing);
inline RichTextBuilder& operator<<(OutlineColorWrapper outlineColor);
inline RichTextBuilder& operator<<(OutlineThicknessWrapper outlineThickness);
inline RichTextBuilder& operator<<(TextStyleFlags textStyle);
static inline CharacterSizeWrapper CharacterSize(unsigned int characterSize);
static inline CharacterSpacingOffsetWrapper CharacterSpacingOffset(unsigned int spacingOffset);
static inline LineSpacingOffsetWrapper LineSpacingOffset(float spacingOffset);
static inline OutlineColorWrapper OutlineColor(const Color& color);
static inline OutlineThicknessWrapper OutlineThickness(float thickness);
struct CharacterSizeWrapper { unsigned int characterSize; };
struct CharacterSpacingOffsetWrapper { float spacingOffset; };
struct LineSpacingOffsetWrapper { float spacingOffset; };
struct OutlineColorWrapper { const Color& color; };
struct OutlineThicknessWrapper { float thickness; };
private:
T& m_richText;
};
}
#include <Nazara/TextRenderer/RichTextBuilder.inl>
#endif // NAZARA_TEXTRENDERER_RICHTEXTBUILDER_HPP

View File

@@ -0,0 +1,169 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/TextRenderer/Debug.hpp>
namespace Nz
{
template<typename T>
RichTextBuilder<T>::RichTextBuilder(T& richText) :
m_richText(richText)
{
}
template<typename T>
RichTextBuilder<T>::RichTextBuilder(T* richText) :
m_richText(*richText)
{
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::AppendText(std::string_view text)
{
m_richText.AppendText(text);
return *this;
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::SetCharacterSize(unsigned int characterSize)
{
m_richText.SetCharacterSize(characterSize);
return *this;
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::SetCharacterSpacingOffset(float offset)
{
m_richText.SetCharacterSpacingOffset(offset);
return *this;
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::SetLineSpacingOffset(float offset)
{
m_richText.SetLineSpacingOffset(offset);
return *this;
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::SetTextColor(const Color& color)
{
m_richText.SetTextColor(color);
return *this;
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::SetTextFont(const std::shared_ptr<Font>& font)
{
m_richText.SetTextFont(font);
return *this;
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::SetTextOutlineColor(const Color& color)
{
m_richText.SetTextOutlineColor(color);
return *this;
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::SetTextOutlineThickness(float thickness)
{
m_richText.SetTextOutlineThickness(thickness);
return *this;
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::SetTextStyle(TextStyleFlags style)
{
m_richText.SetTextStyle(style);
return *this;
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::operator<<(const Color& textColor)
{
return SetTextColor(textColor);
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::operator<<(const std::shared_ptr<Font>& font)
{
return SetTextFont(font);
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::operator<<(std::string_view str)
{
return AppendText(str);
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::operator<<(CharacterSizeWrapper characterSize)
{
return SetCharacterSize(characterSize.characterSize);
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::operator<<(CharacterSpacingOffsetWrapper characterSpacingOffset)
{
return SetCharacterSpacingOffset(characterSpacingOffset.spacingOffset);
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::operator<<(LineSpacingOffsetWrapper lineSpacing)
{
return SetLineSpacingOffset(lineSpacing.spacingOffset);
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::operator<<(OutlineColorWrapper outlineColor)
{
return SetTextOutlineColor(outlineColor.color);
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::operator<<(OutlineThicknessWrapper outlineThickness)
{
return SetTextOutlineThickness(outlineThickness.thickness);
}
template<typename T>
RichTextBuilder<T>& RichTextBuilder<T>::operator<<(TextStyleFlags textStyle)
{
return SetTextStyle(textStyle);
}
template<typename T>
auto RichTextBuilder<T>::CharacterSize(unsigned int characterSize) -> CharacterSizeWrapper
{
return { characterSize };
}
template<typename T>
auto RichTextBuilder<T>::CharacterSpacingOffset(unsigned int spacingOffset) -> CharacterSpacingOffsetWrapper
{
return { spacingOffset };
}
template<typename T>
auto RichTextBuilder<T>::LineSpacingOffset(float spacingOffset) -> LineSpacingOffsetWrapper
{
return { spacingOffset };
}
template<typename T>
auto RichTextBuilder<T>::OutlineColor(const Color& color) -> OutlineColorWrapper
{
return { color };
}
template<typename T>
auto RichTextBuilder<T>::OutlineThickness(float thickness) -> OutlineThicknessWrapper
{
return { thickness };
}
}
#include <Nazara/TextRenderer/DebugOff.hpp>

View File

@@ -0,0 +1,212 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TEXTRENDERER_RICHTEXTDRAWER_HPP
#define NAZARA_TEXTRENDERER_RICHTEXTDRAWER_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Core/Enums.hpp>
#include <Nazara/TextRenderer/AbstractTextDrawer.hpp>
#include <Nazara/TextRenderer/Font.hpp>
#include <string>
#include <vector>
namespace Nz
{
class NAZARA_TEXTRENDERER_API RichTextDrawer : public AbstractTextDrawer
{
public:
class BlockRef;
RichTextDrawer();
RichTextDrawer(const RichTextDrawer& drawer);
RichTextDrawer(RichTextDrawer&& drawer) noexcept;
~RichTextDrawer();
BlockRef AppendText(std::string_view str, bool forceNewBlock = false);
void Clear() override;
inline std::size_t FindBlock(std::size_t glyphIndex) const;
inline unsigned int GetBlockCharacterSize(std::size_t index) const;
inline float GetBlockCharacterSpacingOffset(std::size_t index) const;
inline const Color& GetBlockColor(std::size_t index) const;
inline std::size_t GetBlockCount() const;
inline std::size_t GetBlockFirstGlyphIndex(std::size_t index) const;
inline const std::shared_ptr<Font>& GetBlockFont(std::size_t index) const;
inline float GetBlockLineSpacingOffset(std::size_t index) const;
inline const Color& GetBlockOutlineColor(std::size_t index) const;
inline float GetBlockOutlineThickness(std::size_t index) const;
inline TextStyleFlags GetBlockStyle(std::size_t index) const;
inline const std::string& GetBlockText(std::size_t index) const;
inline BlockRef GetBlock(std::size_t index);
const Rectf& GetBounds() const override;
inline unsigned int GetCharacterSize() const;
inline float GetCharacterSpacingOffset() const;
inline float GetLineSpacingOffset() const;
const std::shared_ptr<Font>& GetFont(std::size_t index) const override;
std::size_t GetFontCount() const override;
const Glyph& GetGlyph(std::size_t index) const override;
std::size_t GetGlyphCount() const override;
const Line& GetLine(std::size_t index) const override;
std::size_t GetLineCount() const override;
float GetMaxLineWidth() const override;
inline const Color& GetTextColor() const;
inline const std::shared_ptr<Font>& GetTextFont() const;
inline const Color& GetTextOutlineColor() const;
inline float GetTextOutlineThickness() const;
inline TextStyleFlags GetTextStyle() const;
inline bool HasBlocks() const;
void MergeBlocks();
void RemoveBlock(std::size_t index);
inline void SetBlockCharacterSize(std::size_t index, unsigned int characterSize);
inline void SetBlockCharacterSpacingOffset(std::size_t index, float offset);
inline void SetBlockColor(std::size_t index, const Color& color);
inline void SetBlockFont(std::size_t index, std::shared_ptr<Font> font);
inline void SetBlockLineSpacingOffset(std::size_t index, float offset);
inline void SetBlockOutlineColor(std::size_t index, const Color& color);
inline void SetBlockOutlineThickness(std::size_t index, float thickness);
inline void SetBlockStyle(std::size_t index, TextStyleFlags style);
inline void SetBlockText(std::size_t index, std::string str);
inline void SetCharacterSize(unsigned int characterSize);
inline void SetCharacterSpacingOffset(float offset);
inline void SetLineSpacingOffset(float offset);
inline void SetTextColor(const Color& color);
inline void SetTextFont(const std::shared_ptr<Font>& font);
inline void SetTextOutlineColor(const Color& color);
inline void SetTextOutlineThickness(float thickness);
inline void SetTextStyle(TextStyleFlags style);
void SetMaxLineWidth(float lineWidth) override;
RichTextDrawer& operator=(const RichTextDrawer& drawer);
RichTextDrawer& operator=(RichTextDrawer&& drawer) noexcept;
static constexpr std::size_t InvalidBlockIndex = std::numeric_limits<std::size_t>::max();
private:
struct Block;
inline void AppendNewLine(const Font& font, unsigned int characterSize, float lineSpacingOffset) const;
void AppendNewLine(const Font& font, unsigned int characterSize, float lineSpacingOffset, std::size_t glyphIndex, float glyphPosition) const;
inline void ClearGlyphs() const;
inline void ConnectFontSlots();
inline void DisconnectFontSlots();
bool GenerateGlyph(Glyph& glyph, char32_t character, float outlineThickness, bool lineWrap, const Font& font, const Color& color, TextStyleFlags style, float lineSpacingOffset, unsigned int characterSize, int renderOrder, int* advance) const;
void GenerateGlyphs(const Font& font, const Color& color, TextStyleFlags style, unsigned int characterSize, const Color& outlineColor, float characterSpacingOffset, float lineSpacingOffset, float outlineThickness, std::string_view text) const;
inline float GetLineHeight(const Block& block) const;
inline float GetLineHeight(float lineSpacingOffset, const Font::SizeInfo& sizeInfo) const;
inline std::size_t HandleFontAddition(const std::shared_ptr<Font>& font);
inline void InvalidateGlyphs();
inline void ReleaseFont(std::size_t fontIndex);
inline bool ShouldLineWrap(float size) const;
void OnFontAtlasLayerChanged(const Font* font, AbstractImage* oldLayer, AbstractImage* newLayer);
void OnFontInvalidated(const Font* font);
void OnFontRelease(const Font* object);
void UpdateGlyphs() const;
static constexpr std::size_t InvalidGlyph = std::numeric_limits<std::size_t>::max();
struct Block
{
std::size_t fontIndex;
std::size_t glyphIndex;
std::string text;
Color color;
Color outlineColor;
TextStyleFlags style;
float characterSpacingOffset;
float lineSpacingOffset;
float outlineThickness;
unsigned int characterSize;
};
struct FontData
{
std::shared_ptr<Font> font;
std::size_t useCount = 0;
NazaraSlot(Font, OnFontAtlasChanged, atlasChangedSlot);
NazaraSlot(Font, OnFontAtlasLayerChanged, atlasLayerChangedSlot);
NazaraSlot(Font, OnFontGlyphCacheCleared, glyphCacheClearedSlot);
NazaraSlot(Font, OnFontRelease, fontReleaseSlot);
};
Color m_currentColor;
Color m_currentOutlineColor;
TextStyleFlags m_currentStyle;
std::shared_ptr<Font> m_currentFont;
mutable std::size_t m_lastSeparatorGlyph;
std::unordered_map<std::shared_ptr<Font>, std::size_t> m_fontIndexes;
std::vector<Block> m_blocks;
std::vector<FontData> m_fonts;
mutable std::vector<Glyph> m_glyphs;
mutable std::vector<Line> m_lines;
mutable Rectf m_bounds;
mutable Vector2f m_drawPos;
mutable bool m_glyphUpdated;
float m_currentCharacterSpacingOffset;
float m_currentLineSpacingOffset;
float m_currentOutlineThickness;
float m_maxLineWidth;
unsigned int m_currentCharacterSize;
mutable float m_lastSeparatorPosition;
};
class RichTextDrawer::BlockRef
{
friend RichTextDrawer;
public:
BlockRef(const BlockRef&) = default;
BlockRef(BlockRef&&) = delete;
~BlockRef() = default;
inline float GetCharacterSpacingOffset() const;
inline unsigned int GetCharacterSize() const;
inline Color GetColor() const;
inline std::size_t GetFirstGlyphIndex() const;
inline const std::shared_ptr<Font>& GetFont() const;
inline float GetLineSpacingOffset() const;
inline Color GetOutlineColor() const;
inline float GetOutlineThickness() const;
inline TextStyleFlags GetStyle() const;
inline const std::string& GetText() const;
inline void SetCharacterSpacingOffset(float offset);
inline void SetCharacterSize(unsigned int size);
inline void SetColor(Color color);
inline void SetFont(std::shared_ptr<Font> font);
inline void SetLineSpacingOffset(float offset);
inline void SetOutlineColor(Color color);
inline void SetOutlineThickness(float thickness);
inline void SetStyle(TextStyleFlags style);
inline void SetText(std::string text);
BlockRef& operator=(const BlockRef&) = delete;
BlockRef& operator=(BlockRef&&) = delete;
private:
inline BlockRef(RichTextDrawer& drawer, std::size_t index);
std::size_t m_blockIndex;
RichTextDrawer& m_drawer;
};
}
#include <Nazara/TextRenderer/RichTextDrawer.inl>
#endif // NAZARA_TEXTRENDERER_RICHTEXTDRAWER_HPP

View File

@@ -0,0 +1,623 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/TextRenderer/Debug.hpp>
namespace Nz
{
inline std::size_t RichTextDrawer::FindBlock(std::size_t glyphIndex) const
{
auto it = m_blocks.begin();
for (; it != m_blocks.end(); ++it)
{
if (it->glyphIndex > glyphIndex)
break;
}
assert(it != m_blocks.begin());
return std::distance(m_blocks.begin(), it) - 1;
/*
// Binary search
std::size_t count = m_blocks.size();
std::size_t step;
std::size_t i = InvalidBlockIndex;
std::size_t first = 0;
std::size_t last = count;
while (count > 0)
{
i = first;
step = count / 2;
i += step;
if (m_blocks[i].glyphIndex < glyphIndex)
{
first = ++i;
count -= step + 1;
}
else
count = step;
}
return i;*/
}
inline auto RichTextDrawer::GetBlock(std::size_t index) -> BlockRef
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return BlockRef(*this, index);
}
inline unsigned int RichTextDrawer::GetBlockCharacterSize(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].characterSize;
}
inline float RichTextDrawer::GetBlockCharacterSpacingOffset(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].characterSpacingOffset;
}
inline const Color& RichTextDrawer::GetBlockColor(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].color;
}
inline std::size_t RichTextDrawer::GetBlockCount() const
{
return m_blocks.size();
}
inline std::size_t RichTextDrawer::GetBlockFirstGlyphIndex(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].glyphIndex;
}
inline const std::shared_ptr<Font>& RichTextDrawer::GetBlockFont(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
std::size_t fontIndex = m_blocks[index].fontIndex;
assert(fontIndex < m_fonts.size());
return m_fonts[fontIndex].font;
}
inline float RichTextDrawer::GetBlockLineSpacingOffset(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].lineSpacingOffset;
}
inline const Color& RichTextDrawer::GetBlockOutlineColor(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].outlineColor;
}
inline float RichTextDrawer::GetBlockOutlineThickness(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].outlineThickness;
}
inline TextStyleFlags RichTextDrawer::GetBlockStyle(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].style;
}
inline const std::string& RichTextDrawer::GetBlockText(std::size_t index) const
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
return m_blocks[index].text;
}
inline unsigned int RichTextDrawer::GetCharacterSize() const
{
return m_currentCharacterSize;
}
inline float RichTextDrawer::GetCharacterSpacingOffset() const
{
return m_currentCharacterSpacingOffset;
}
inline float RichTextDrawer::GetLineSpacingOffset() const
{
return m_currentLineSpacingOffset;
}
inline void RichTextDrawer::AppendNewLine(const Font& font, unsigned int characterSize, float lineSpacingOffset) const
{
AppendNewLine(font, characterSize, lineSpacingOffset, InvalidGlyph, 0);
}
inline void RichTextDrawer::ClearGlyphs() const
{
m_bounds = Rectf::Zero(); //< Compute bounds as float to speedup bounds computation (as casting between floats and integers is costly)
m_lastSeparatorGlyph = InvalidGlyph;
m_lines.clear();
m_glyphs.clear();
m_glyphUpdated = true;
}
inline void RichTextDrawer::ConnectFontSlots()
{
for (auto& fontData : m_fonts)
{
fontData.atlasChangedSlot.Connect(fontData.font->OnFontAtlasChanged, this, &RichTextDrawer::OnFontInvalidated);
fontData.atlasLayerChangedSlot.Connect(fontData.font->OnFontAtlasLayerChanged, this, &RichTextDrawer::OnFontAtlasLayerChanged);
fontData.fontReleaseSlot.Connect(fontData.font->OnFontDestroy, this, &RichTextDrawer::OnFontRelease);
fontData.glyphCacheClearedSlot.Connect(fontData.font->OnFontGlyphCacheCleared, this, &RichTextDrawer::OnFontInvalidated);
}
}
inline void RichTextDrawer::DisconnectFontSlots()
{
for (auto& fontData : m_fonts)
{
fontData.atlasChangedSlot.Disconnect();
fontData.atlasLayerChangedSlot.Disconnect();
fontData.fontReleaseSlot.Disconnect();
fontData.glyphCacheClearedSlot.Disconnect();
}
}
inline float RichTextDrawer::GetLineHeight(const Block& block) const
{
assert(block.fontIndex < m_fonts.size());
const FontData& fontData = m_fonts[block.fontIndex];
return GetLineHeight(block.lineSpacingOffset, fontData.font->GetSizeInfo(block.characterSize));
}
inline float RichTextDrawer::GetLineHeight(float lineSpacingOffset, const Font::SizeInfo& sizeInfo) const
{
return float(sizeInfo.lineHeight) + lineSpacingOffset;
}
inline std::size_t RichTextDrawer::HandleFontAddition(const std::shared_ptr<Font>& font)
{
auto it = m_fontIndexes.find(font);
if (it == m_fontIndexes.end())
{
std::size_t fontIndex = m_fonts.size();
m_fonts.emplace_back();
auto& fontData = m_fonts.back();
fontData.font = font;
fontData.atlasChangedSlot.Connect(font->OnFontAtlasChanged, this, &RichTextDrawer::OnFontInvalidated);
fontData.atlasLayerChangedSlot.Connect(font->OnFontAtlasLayerChanged, this, &RichTextDrawer::OnFontAtlasLayerChanged);
fontData.fontReleaseSlot.Connect(font->OnFontDestroy, this, &RichTextDrawer::OnFontRelease);
fontData.glyphCacheClearedSlot.Connect(font->OnFontGlyphCacheCleared, this, &RichTextDrawer::OnFontInvalidated);
it = m_fontIndexes.emplace(font, fontIndex).first;
}
return it->second;
}
inline void RichTextDrawer::ReleaseFont(std::size_t fontIndex)
{
assert(fontIndex < m_fonts.size());
FontData& fontData = m_fonts[fontIndex];
assert(fontData.useCount > 0);
if (--fontData.useCount == 0)
{
// Shift font indexes
m_fontIndexes.erase(fontData.font);
for (auto& fontIndexe : m_fontIndexes)
{
if (fontIndexe.second > fontIndex)
fontIndexe.second--;
}
m_fonts.erase(m_fonts.begin() + fontIndex);
}
}
inline bool RichTextDrawer::ShouldLineWrap(float size) const
{
if (m_lines.back().glyphIndex > m_glyphs.size())
return false;
return m_lines.back().bounds.GetMaximum().x + size > m_maxLineWidth;
}
inline const Color& RichTextDrawer::GetTextColor() const
{
return m_currentColor;
}
inline const std::shared_ptr<Font>& RichTextDrawer::GetTextFont() const
{
return m_currentFont;
}
inline const Color& RichTextDrawer::GetTextOutlineColor() const
{
return m_currentOutlineColor;
}
inline float RichTextDrawer::GetTextOutlineThickness() const
{
return m_currentOutlineThickness;
}
inline TextStyleFlags RichTextDrawer::GetTextStyle() const
{
return m_currentStyle;
}
inline bool RichTextDrawer::HasBlocks() const
{
return !m_blocks.empty();
}
inline void RichTextDrawer::SetBlockCharacterSize(std::size_t index, unsigned int characterSize)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
m_blocks[index].characterSize = characterSize;
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockCharacterSpacingOffset(std::size_t index, float offset)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
m_blocks[index].characterSpacingOffset = offset;
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockColor(std::size_t index, const Color& color)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
m_blocks[index].color = color;
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockFont(std::size_t index, std::shared_ptr<Font> font)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
std::size_t fontIndex = HandleFontAddition(font);
std::size_t oldFontIndex = m_blocks[index].fontIndex;
if (oldFontIndex != fontIndex)
{
ReleaseFont(oldFontIndex);
m_fonts[fontIndex].useCount++;
m_blocks[index].fontIndex = fontIndex;
}
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockLineSpacingOffset(std::size_t index, float offset)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
m_blocks[index].lineSpacingOffset = offset;
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockOutlineColor(std::size_t index, const Color& color)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
m_blocks[index].outlineColor = color;
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockOutlineThickness(std::size_t index, float thickness)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
m_blocks[index].outlineThickness = thickness;
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockStyle(std::size_t index, TextStyleFlags style)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
m_blocks[index].style = style;
InvalidateGlyphs();
}
inline void RichTextDrawer::SetBlockText(std::size_t index, std::string str)
{
NazaraAssert(index < m_blocks.size(), "Invalid block index");
std::size_t previousLength = m_blocks[index].text.size(); //< FIXME: Count Unicode glyphs
m_blocks[index].text = std::move(str);
std::size_t newLength = m_blocks[index].text.size(); //< FIXME: Count Unicode glyphs
if (newLength != previousLength)
{
std::size_t delta = newLength - previousLength; //< Underflow allowed
for (std::size_t i = index + 1; i < m_blocks.size(); ++i)
m_blocks[i].glyphIndex += delta;
}
InvalidateGlyphs();
}
inline void RichTextDrawer::SetCharacterSize(unsigned int characterSize)
{
m_currentCharacterSize = characterSize;
}
inline void RichTextDrawer::SetCharacterSpacingOffset(float offset)
{
m_currentCharacterSpacingOffset = offset;
}
inline void RichTextDrawer::SetLineSpacingOffset(float offset)
{
m_currentLineSpacingOffset = offset;
}
inline void RichTextDrawer::SetTextColor(const Color& color)
{
m_currentColor = color;
}
inline void RichTextDrawer::SetTextFont(const std::shared_ptr<Font>& font)
{
m_currentFont = font;
}
inline void RichTextDrawer::SetTextOutlineColor(const Color& color)
{
m_currentOutlineColor = color;
}
inline void RichTextDrawer::SetTextOutlineThickness(float thickness)
{
m_currentOutlineThickness = thickness;
}
inline void RichTextDrawer::SetTextStyle(TextStyleFlags style)
{
m_currentStyle = style;
}
inline void RichTextDrawer::InvalidateGlyphs()
{
m_glyphUpdated = false;
}
/*!
* \class Nz::RichTextDrawer::BlockRef
* \brief Helper class representing a block inside a RichTextDrawer, allowing easier access.
*
* \warning This class is meant for temporary use, moving or destroying the RichTextDrawer or one of its blocks invalidates all BlockRef
*/
inline RichTextDrawer::BlockRef::BlockRef(RichTextDrawer& drawer, std::size_t index) :
m_blockIndex(index),
m_drawer(drawer)
{
}
/*!
* Returns the character spacing offset used for the characters of the referenced block
* \return The referenced block character size
*
* \see GetColor, GetFont, GetStyle, GetText, SetCharacterSize
*/
inline float RichTextDrawer::BlockRef::GetCharacterSpacingOffset() const
{
return m_drawer.GetBlockCharacterSpacingOffset(m_blockIndex);
}
/*!
* Returns the character size used for the characters of the referenced block
* \return The referenced block character size
*
* \see GetColor, GetFont, GetStyle, GetText, SetCharacterSize
*/
inline unsigned int RichTextDrawer::BlockRef::GetCharacterSize() const
{
return m_drawer.GetBlockCharacterSize(m_blockIndex);
}
/*!
* Returns the color used for the characters of the referenced block
* \return The referenced block color
*
* \see GetCharacterSize, GetFont, GetStyle, GetText, SetColor
*/
inline Color RichTextDrawer::BlockRef::GetColor() const
{
return m_drawer.GetBlockColor(m_blockIndex);
}
/*!
* Returns the font used for the characters of the referenced block
* \return A reference on the referenced block font
*
* \see GetCharacterSize, GetTextColor, GetStyle, GetText, SetFont
*/
inline const std::shared_ptr<Font>& RichTextDrawer::BlockRef::GetFont() const
{
return m_drawer.GetBlockFont(m_blockIndex);
}
/*!
* Returns the line spacing offset used for the characters of the referenced block
* \return The referenced block character size
*
* \see GetTextColor, GetTextFont, GetStyle, GetText, SetCharacterSize
*/
inline float RichTextDrawer::BlockRef::GetLineSpacingOffset() const
{
return m_drawer.GetBlockLineSpacingOffset(m_blockIndex);
}
/*!
* Returns the outline color used for the characters of the referenced block
* \return The referenced block outline color
*
* \see GetCharacterSize, GetTextColor, GetStyle, GetText, SetFont
*/
inline Color RichTextDrawer::BlockRef::GetOutlineColor() const
{
return m_drawer.GetBlockOutlineColor(m_blockIndex);
}
/*!
* Returns the outline thickness used for the characters of the referenced block
* \return The referenced block outline thickness
*
* \see GetCharacterSize, GetTextColor, GetStyle, GetText, SetFont
*/
inline float RichTextDrawer::BlockRef::GetOutlineThickness() const
{
return m_drawer.GetBlockOutlineThickness(m_blockIndex);
}
/*!
* Returns the style flags used for the characters of the referenced block
* \return The referenced block style flags (see TextStyleFlags)
*
* \see GetCharacterSize, GetTextColor, GetTextFont, GetText, SetStyle
*/
inline TextStyleFlags RichTextDrawer::BlockRef::GetStyle() const
{
return m_drawer.GetBlockStyle(m_blockIndex);
}
/*!
* Returns the first glyph index at which starts the referenced block
* \return The first glyph index concerned by this block
*
* \see GetText
*/
inline std::size_t RichTextDrawer::BlockRef::GetFirstGlyphIndex() const
{
return m_drawer.GetBlockFirstGlyphIndex(m_blockIndex);
}
/*!
* Returns the text of the referenced block
* \return The referenced block text
*
* \see GetCharacterSize, GetTextColor, GetTextFont, GetStyle, SetText
*/
inline const std::string& RichTextDrawer::BlockRef::GetText() const
{
return m_drawer.GetBlockText(m_blockIndex);
}
/*!
* Changes the character spacing offset of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetCharacterSpacingOffset, SetColor, SetFont, SetStyle, SetText
*/
inline void RichTextDrawer::BlockRef::SetCharacterSpacingOffset(float offset)
{
m_drawer.SetBlockCharacterSpacingOffset(m_blockIndex, offset);
}
/*!
* Changes the character size of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetCharacterSize, SetColor, SetFont, SetStyle, SetText
*/
inline void RichTextDrawer::BlockRef::SetCharacterSize(unsigned int size)
{
m_drawer.SetBlockCharacterSize(m_blockIndex, size);
}
/*!
* Changes the color of the referenced block characters
* \remark This is the only property that can be changed without forcing a glyph regeneration
*
* \see GetTextColor, SetCharacterSize, SetFont, SetStyle, SetText
*/
inline void RichTextDrawer::BlockRef::SetColor(Color color)
{
m_drawer.SetBlockColor(m_blockIndex, color);
}
/*!
* Changes the font of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetCharacterSize, SetCharacterSize, SetTextColor, SetStyle, SetText
*/
inline void RichTextDrawer::BlockRef::SetFont(std::shared_ptr<Font> font)
{
m_drawer.SetBlockFont(m_blockIndex, std::move(font));
}
/*!
* Changes the line spacing offset of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetLineSpacingOffset, SetTextColor, SetTextFont, SetStyle, SetText
*/
inline void RichTextDrawer::BlockRef::SetLineSpacingOffset(float offset)
{
m_drawer.SetBlockLineSpacingOffset(m_blockIndex, offset);
}
/*!
* Changes the outline color of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetCharacterSize, SetCharacterSize, SetTextColor, SetStyle, SetText
*/
inline void RichTextDrawer::BlockRef::SetOutlineColor(Color color)
{
m_drawer.SetBlockOutlineColor(m_blockIndex, std::move(color));
}
/*!
* Changes the outline thickness of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetCharacterSize, SetCharacterSize, SetTextColor, SetStyle, SetText
*/
inline void RichTextDrawer::BlockRef::SetOutlineThickness(float thickness)
{
m_drawer.SetBlockOutlineThickness(m_blockIndex, thickness);
}
/*!
* Changes the style flags of the referenced block characters
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetStyle, SetCharacterSize, SetTextColor, SetTextFont, SetText
*/
inline void RichTextDrawer::BlockRef::SetStyle(TextStyleFlags style)
{
m_drawer.SetBlockStyle(m_blockIndex, style);
}
/*!
* Changes the text of the referenced block
* \remark This invalidates the drawer and will force a (complete or partial, depending on the block index) glyph regeneration to occur.
*
* \see GetText, SetCharacterSize, SetTextColor, SetTextFont, SetTextStyle
*/
inline void RichTextDrawer::BlockRef::SetText(std::string text)
{
m_drawer.SetBlockText(m_blockIndex, std::move(text));
}
}
#include <Nazara/TextRenderer/DebugOff.hpp>

View File

@@ -0,0 +1,126 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TEXTRENDERER_SIMPLETEXTDRAWER_HPP
#define NAZARA_TEXTRENDERER_SIMPLETEXTDRAWER_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Core/Enums.hpp>
#include <Nazara/TextRenderer/AbstractTextDrawer.hpp>
#include <Nazara/TextRenderer/Font.hpp>
#include <vector>
namespace Nz
{
class NAZARA_TEXTRENDERER_API SimpleTextDrawer : public AbstractTextDrawer
{
public:
inline SimpleTextDrawer();
inline SimpleTextDrawer(const SimpleTextDrawer& drawer);
inline SimpleTextDrawer(SimpleTextDrawer&& drawer) noexcept;
~SimpleTextDrawer() = default;
inline void AppendText(std::string_view str);
void Clear() override;
const Rectf& GetBounds() const override;
inline float GetCharacterSpacingOffset() const;
inline unsigned int GetCharacterSize() const;
const std::shared_ptr<Font>& GetFont(std::size_t index) const override;
std::size_t GetFontCount() const override;
const Glyph& GetGlyph(std::size_t index) const override;
std::size_t GetGlyphCount() const override;
const Line& GetLine(std::size_t index) const override;
std::size_t GetLineCount() const override;
inline float GetLineHeight() const;
inline float GetLineSpacingOffset() const;
float GetMaxLineWidth() const override;
inline const std::string& GetText() const;
inline const Color& GetTextColor() const;
inline const std::shared_ptr<Font>& GetTextFont() const;
inline const Color& GetTextOutlineColor() const;
inline float GetTextOutlineThickness() const;
inline TextStyleFlags GetTextStyle() const;
inline void SetCharacterSpacingOffset(float offset);
inline void SetCharacterSize(unsigned int characterSize);
inline void SetLineSpacingOffset(float offset);
inline void SetMaxLineWidth(float lineWidth) override;
inline void SetText(std::string str);
inline void SetTextColor(const Color& color);
inline void SetTextFont(std::shared_ptr<Font> font);
inline void SetTextOutlineColor(const Color& color);
inline void SetTextOutlineThickness(float thickness);
inline void SetTextStyle(TextStyleFlags style);
inline SimpleTextDrawer& operator=(const SimpleTextDrawer& drawer);
inline SimpleTextDrawer& operator=(SimpleTextDrawer&& drawer) noexcept;
static inline SimpleTextDrawer Draw(std::string str, unsigned int characterSize, TextStyleFlags style = TextStyle_Regular, const Color& color = Color::White());
static inline SimpleTextDrawer Draw(std::string str, unsigned int characterSize, TextStyleFlags style, const Color& color, float outlineThickness, const Color& outlineColor);
static inline SimpleTextDrawer Draw(const std::shared_ptr<Font>& font, std::string str, unsigned int characterSize, TextStyleFlags style = TextStyle_Regular, const Color& color = Color::White());
static inline SimpleTextDrawer Draw(const std::shared_ptr<Font>& font, std::string str, unsigned int characterSize, TextStyleFlags style, const Color& color, float outlineThickness, const Color& outlineColor);
private:
inline void AppendNewLine() const;
void AppendNewLine(std::size_t glyphIndex, float glyphPosition) const;
void ClearGlyphs() const;
inline void ConnectFontSlots();
inline void DisconnectFontSlots();
bool GenerateGlyph(Glyph& glyph, char32_t character, float outlineThickness, bool lineWrap, Color color, int renderOrder, int* advance) const;
void GenerateGlyphs(std::string_view text) const;
inline float GetLineHeight(const Font::SizeInfo& sizeInfo) const;
inline void InvalidateColor();
inline void InvalidateGlyphs();
void OnFontAtlasLayerChanged(const Font* font, AbstractImage* oldLayer, AbstractImage* newLayer);
void OnFontInvalidated(const Font* font);
void OnFontRelease(const Font* object);
inline bool ShouldLineWrap(float size) const;
inline void UpdateGlyphColor() const;
inline void UpdateGlyphs() const;
static constexpr std::size_t InvalidGlyph = std::numeric_limits<std::size_t>::max();
NazaraSlot(Font, OnFontAtlasChanged, m_atlasChangedSlot);
NazaraSlot(Font, OnFontAtlasLayerChanged, m_atlasLayerChangedSlot);
NazaraSlot(Font, OnFontGlyphCacheCleared, m_glyphCacheClearedSlot);
NazaraSlot(Font, OnFontRelease, m_fontReleaseSlot);
mutable std::size_t m_lastSeparatorGlyph;
mutable std::vector<Glyph> m_glyphs;
mutable std::vector<Line> m_lines;
std::string m_text;
Color m_color;
Color m_outlineColor;
std::shared_ptr<Font> m_font;
mutable Rectf m_bounds;
TextStyleFlags m_style;
mutable UInt32 m_previousCharacter;
mutable Vector2f m_drawPos;
mutable bool m_colorUpdated;
mutable bool m_glyphUpdated;
mutable float m_lastSeparatorPosition;
float m_characterSpacingOffset;
float m_lineSpacingOffset;
float m_maxLineWidth;
float m_outlineThickness;
unsigned int m_characterSize;
};
}
#include <Nazara/TextRenderer/SimpleTextDrawer.inl>
#endif // NAZARA_TEXTRENDERER_SIMPLETEXTDRAWER_HPP

View File

@@ -0,0 +1,384 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/TextRenderer/Debug.hpp>
namespace Nz
{
inline SimpleTextDrawer::SimpleTextDrawer() :
m_color(Color::White()),
m_outlineColor(Color::Black()),
m_style(TextStyle_Regular),
m_colorUpdated(true),
m_glyphUpdated(true),
m_characterSpacingOffset(0.f),
m_lineSpacingOffset(0.f),
m_maxLineWidth(std::numeric_limits<float>::infinity()),
m_outlineThickness(0.f),
m_characterSize(24)
{
SetTextFont(Font::GetDefault());
}
inline SimpleTextDrawer::SimpleTextDrawer(const SimpleTextDrawer& drawer) :
m_text(drawer.m_text),
m_color(drawer.m_color),
m_outlineColor(drawer.m_outlineColor),
m_style(drawer.m_style),
m_colorUpdated(false),
m_glyphUpdated(false),
m_characterSpacingOffset(drawer.m_characterSpacingOffset),
m_lineSpacingOffset(drawer.m_lineSpacingOffset),
m_maxLineWidth(drawer.m_maxLineWidth),
m_outlineThickness(drawer.m_outlineThickness),
m_characterSize(drawer.m_characterSize)
{
SetTextFont(drawer.m_font);
}
inline SimpleTextDrawer::SimpleTextDrawer(SimpleTextDrawer&& drawer) noexcept
{
operator=(std::move(drawer));
}
inline void SimpleTextDrawer::AppendText(std::string_view str)
{
m_text.append(str);
if (m_glyphUpdated)
GenerateGlyphs(str);
}
inline float SimpleTextDrawer::GetCharacterSpacingOffset() const
{
return m_characterSpacingOffset;
}
inline unsigned int SimpleTextDrawer::GetCharacterSize() const
{
return m_characterSize;
}
inline float SimpleTextDrawer::GetLineHeight() const
{
NazaraAssert(m_font, "SimpleTextDrawer has no font");
return GetLineHeight(m_font->GetSizeInfo(m_characterSize));
}
inline float SimpleTextDrawer::GetLineSpacingOffset() const
{
return m_lineSpacingOffset;
}
inline const std::string& SimpleTextDrawer::GetText() const
{
return m_text;
}
inline const Color& SimpleTextDrawer::GetTextColor() const
{
return m_color;
}
inline const std::shared_ptr<Font>& SimpleTextDrawer::GetTextFont() const
{
return m_font;
}
inline const Color& SimpleTextDrawer::GetTextOutlineColor() const
{
return m_outlineColor;
}
inline float SimpleTextDrawer::GetTextOutlineThickness() const
{
return m_outlineThickness;
}
inline TextStyleFlags SimpleTextDrawer::GetTextStyle() const
{
return m_style;
}
inline void SimpleTextDrawer::SetCharacterSpacingOffset(float offset)
{
if (m_characterSpacingOffset != offset)
{
m_characterSpacingOffset = offset;
InvalidateGlyphs();
}
}
inline void SimpleTextDrawer::SetCharacterSize(unsigned int characterSize)
{
if (m_characterSize != characterSize)
{
m_characterSize = characterSize;
InvalidateGlyphs();
}
}
inline void SimpleTextDrawer::SetLineSpacingOffset(float offset)
{
if (m_lineSpacingOffset != offset)
{
m_lineSpacingOffset = offset;
InvalidateGlyphs();
}
}
inline void SimpleTextDrawer::SetMaxLineWidth(float lineWidth)
{
if (m_maxLineWidth != lineWidth)
{
NazaraAssert(lineWidth > 0.f, "Max line width must be positive");
m_maxLineWidth = lineWidth;
InvalidateGlyphs();
}
}
inline void SimpleTextDrawer::SetText(std::string str)
{
if (m_text != str)
{
m_text = std::move(str);
InvalidateGlyphs();
}
}
inline void SimpleTextDrawer::SetTextColor(const Color& color)
{
if (m_color != color)
{
m_color = color;
InvalidateColor();
}
}
inline void SimpleTextDrawer::SetTextFont(std::shared_ptr<Font> font)
{
if (m_font != font)
{
m_font = std::move(font);
if (m_font)
ConnectFontSlots();
else
DisconnectFontSlots();
InvalidateGlyphs();
}
}
inline void SimpleTextDrawer::SetTextOutlineColor(const Color& color)
{
if (m_outlineColor != color)
{
m_outlineColor = color;
InvalidateColor();
}
}
inline void SimpleTextDrawer::SetTextOutlineThickness(float thickness)
{
if (m_outlineThickness != thickness)
{
NazaraAssert(thickness >= 0.f, "Thickness must be zero or positive");
m_outlineThickness = thickness;
InvalidateGlyphs();
}
}
inline void SimpleTextDrawer::SetTextStyle(TextStyleFlags style)
{
if (m_style != style)
{
m_style = style;
InvalidateGlyphs();
}
}
inline SimpleTextDrawer& SimpleTextDrawer::operator=(const SimpleTextDrawer& drawer)
{
m_characterSize = drawer.m_characterSize;
m_characterSpacingOffset = drawer.m_characterSpacingOffset;
m_color = drawer.m_color;
m_lineSpacingOffset = drawer.m_lineSpacingOffset;
m_maxLineWidth = drawer.m_maxLineWidth;
m_outlineColor = drawer.m_outlineColor;
m_outlineThickness = drawer.m_outlineThickness;
m_style = drawer.m_style;
m_text = drawer.m_text;
SetTextFont(drawer.m_font);
InvalidateGlyphs();
return *this;
}
inline SimpleTextDrawer& SimpleTextDrawer::operator=(SimpleTextDrawer&& drawer) noexcept
{
DisconnectFontSlots();
m_bounds = std::move(drawer.m_bounds);
m_colorUpdated = std::move(drawer.m_colorUpdated);
m_characterSize = std::move(drawer.m_characterSize);
m_characterSpacingOffset = drawer.m_characterSpacingOffset;
m_color = std::move(drawer.m_color);
m_glyphs = std::move(drawer.m_glyphs);
m_glyphUpdated = std::move(drawer.m_glyphUpdated);
m_font = std::move(drawer.m_font);
m_lineSpacingOffset = drawer.m_lineSpacingOffset;
m_maxLineWidth = drawer.m_maxLineWidth;
m_outlineColor = std::move(drawer.m_outlineColor);
m_outlineThickness = std::move(drawer.m_outlineThickness);
m_style = std::move(drawer.m_style);
m_text = std::move(drawer.m_text);
// Update slot pointers (TODO: Improve the way of doing this)
if (m_font)
{
drawer.DisconnectFontSlots();
ConnectFontSlots();
}
return *this;
}
inline SimpleTextDrawer SimpleTextDrawer::Draw(std::string str, unsigned int characterSize, TextStyleFlags style, const Color& color)
{
SimpleTextDrawer drawer;
drawer.SetCharacterSize(characterSize);
drawer.SetTextColor(color);
drawer.SetTextStyle(style);
drawer.SetText(std::move(str));
return drawer;
}
inline SimpleTextDrawer SimpleTextDrawer::Draw(std::string str, unsigned int characterSize, TextStyleFlags style, const Color& color, float outlineThickness, const Color& outlineColor)
{
SimpleTextDrawer drawer;
drawer.SetCharacterSize(characterSize);
drawer.SetTextColor(color);
drawer.SetTextOutlineColor(outlineColor);
drawer.SetTextOutlineThickness(outlineThickness);
drawer.SetTextStyle(style);
drawer.SetText(std::move(str));
return drawer;
}
inline SimpleTextDrawer SimpleTextDrawer::Draw(const std::shared_ptr<Font>& font, std::string str, unsigned int characterSize, TextStyleFlags style, const Color& color)
{
SimpleTextDrawer drawer;
drawer.SetCharacterSize(characterSize);
drawer.SetTextColor(color);
drawer.SetTextFont(font);
drawer.SetTextStyle(style);
drawer.SetText(std::move(str));
return drawer;
}
inline SimpleTextDrawer SimpleTextDrawer::Draw(const std::shared_ptr<Font>& font, std::string str, unsigned int characterSize, TextStyleFlags style, const Color& color, float outlineThickness, const Color& outlineColor)
{
SimpleTextDrawer drawer;
drawer.SetCharacterSize(characterSize);
drawer.SetTextColor(color);
drawer.SetTextFont(font);
drawer.SetTextOutlineColor(outlineColor);
drawer.SetTextOutlineThickness(outlineThickness);
drawer.SetTextStyle(style);
drawer.SetText(std::move(str));
return drawer;
}
inline void SimpleTextDrawer::AppendNewLine() const
{
AppendNewLine(InvalidGlyph, 0);
}
inline void SimpleTextDrawer::ConnectFontSlots()
{
m_atlasChangedSlot.Connect(m_font->OnFontAtlasChanged, this, &SimpleTextDrawer::OnFontInvalidated);
m_atlasLayerChangedSlot.Connect(m_font->OnFontAtlasLayerChanged, this, &SimpleTextDrawer::OnFontAtlasLayerChanged);
m_fontReleaseSlot.Connect(m_font->OnFontRelease, this, &SimpleTextDrawer::OnFontRelease);
m_glyphCacheClearedSlot.Connect(m_font->OnFontGlyphCacheCleared, this, &SimpleTextDrawer::OnFontInvalidated);
}
inline void SimpleTextDrawer::DisconnectFontSlots()
{
m_atlasChangedSlot.Disconnect();
m_atlasLayerChangedSlot.Disconnect();
m_fontReleaseSlot.Disconnect();
m_glyphCacheClearedSlot.Disconnect();
}
inline float SimpleTextDrawer::GetLineHeight(const Font::SizeInfo& sizeInfo) const
{
return float(sizeInfo.lineHeight) + m_lineSpacingOffset;
}
inline void SimpleTextDrawer::InvalidateColor()
{
m_colorUpdated = false;
}
inline void SimpleTextDrawer::InvalidateGlyphs()
{
m_glyphUpdated = false;
}
inline bool SimpleTextDrawer::ShouldLineWrap(float size) const
{
if (m_lines.back().glyphIndex > m_glyphs.size())
return false;
return m_lines.back().bounds.GetMaximum().x + size > m_maxLineWidth;
}
inline void SimpleTextDrawer::UpdateGlyphColor() const
{
if (m_outlineThickness > 0.f)
{
for (std::size_t glyphIndex = 0; glyphIndex < m_glyphs.size(); ++glyphIndex)
{
Glyph& glyph = m_glyphs[glyphIndex];
if (glyphIndex % 2 == 0)
glyph.color = m_outlineColor;
else
glyph.color = m_color;
}
}
else
{
for (Glyph& glyph : m_glyphs)
glyph.color = m_color;
}
m_colorUpdated = true;
}
inline void SimpleTextDrawer::UpdateGlyphs() const
{
NazaraAssert(m_font && m_font->IsValid(), "Invalid font");
ClearGlyphs();
GenerateGlyphs(m_text);
}
}
#include <Nazara/TextRenderer/DebugOff.hpp>

View File

@@ -0,0 +1,39 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Text renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TEXTRENDERER_HPP
#define NAZARA_TEXTRENDERER_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Core/Core.hpp>
#include <Nazara/TextRenderer/Config.hpp>
#include <Nazara/TextRenderer/Font.hpp>
namespace Nz
{
class NAZARA_TEXTRENDERER_API TextRenderer : public ModuleBase<TextRenderer>
{
friend ModuleBase;
public:
using Dependencies = TypeList<Core>;
struct Config {};
TextRenderer(Config /*config*/);
~TextRenderer();
FontLoader& GetFontLoader();
const FontLoader& GetFontLoader() const;
private:
FontLoader m_fontLoader;
static TextRenderer* s_instance;
};
}
#endif // NAZARA_TEXTRENDERER_HPP