Browse Source

Regenerate all layers on new font glyphs

Michael Ragazzon 6 years ago
parent
commit
9875148881

+ 3 - 0
Include/RmlUi/Core/Texture.h

@@ -65,6 +65,9 @@ public:
 	/// @return The texture's dimensions. This will be (0, 0) if the texture isn't loaded.
 	Vector2i GetDimensions(RenderInterface* render_interface) const;
 
+	/// Removes the underlying texture resource from the texture database, thereby releasing the texture once all references to it are removed.
+	void RemoveDatabaseCache() const;
+
 	/// Returns true if the texture points to the same underlying resource.
 	bool operator==(const Texture&) const;
 

+ 49 - 26
Source/Core/FontFaceHandleDefault.cpp

@@ -173,7 +173,8 @@ int FontFaceHandleDefault::GenerateLayerConfiguration(const FontEffectList& font
 			added_base_layer = true;
 		}
 
-		layer_configuration.push_back(GenerateLayer(font_effects[i]));
+		FontFaceLayer* new_layer = GenerateLayer(font_effects[i]);
+		layer_configuration.push_back(new_layer);
 	}
 
 	// Add the base layer now if we still haven't added it.
@@ -193,7 +194,7 @@ bool FontFaceHandleDefault::GenerateLayerTexture(UniquePtr<const byte[]>& textur
 	if (layer_iterator == layers.end())
 		return false;
 
-	return layer_iterator->second->GenerateTexture(texture_data, texture_dimensions, texture_id);
+	return layer_iterator->second->GenerateTexture(glyphs, texture_data, texture_dimensions, texture_id);
 }
 
 // Generates the geometry required to render a single line of text.
@@ -286,25 +287,48 @@ bool FontFaceHandleDefault::UpdateLayersOnDirty()
 		++version;
 
 		// If we are dirty, regenerate the base layer and increment the version
-		// TODO: Regenerate font effects as well.
 		if (base_layer)
 		{
-			// Regenerate the base layer
-			// TODO: This may break some pointers to the base layer.
+			// Regenerate all the layers
+			// TODO: The following is almost a copy-paste of GenerateLayer.
 
-			FontFaceLayer* old_base = base_layer;
-
-			layers.erase(nullptr);
-			base_layer = GenerateLayer(nullptr);
-
-			for (auto& configuration : layer_configurations)
-				for (FontFaceLayer*& layer : configuration)
-					if (layer == old_base)
-						layer = base_layer;
+			for (auto& pair : layers)
+			{
+				const FontEffect* font_effect = pair.first;
+				FontFaceLayer* layer = pair.second.get();
 
-			for (auto& pair : layer_cache)
-				if (pair.second == old_base)
-					pair.second = base_layer;
+				if (!font_effect)
+				{
+					layer->Regenerate(this);
+				}
+				else
+				{
+					// Determine which, if any, layer the new layer should copy its geometry and textures from.
+					FontFaceLayer* clone = nullptr;
+					bool clone_glyph_origins = true;
+					String generation_key;
+					size_t fingerprint = font_effect->GetFingerprint();
+
+					if (!font_effect->HasUniqueTexture())
+					{
+						clone = base_layer;
+						clone_glyph_origins = false;
+					}
+					else
+					{
+						auto cache_iterator = layer_cache.find(fingerprint);
+						if (cache_iterator != layer_cache.end() && cache_iterator->second != layer)
+							clone = cache_iterator->second;
+					}
+
+					// Create a new layer.
+					layer->Regenerate(this, clone, clone_glyph_origins);
+
+					// Cache the layer in the layer cache if it generated its own textures (ie, didn't clone).
+					if (!clone)
+						layer_cache[fingerprint] = layer;
+				}
+			}
 		}
 	}
 
@@ -404,9 +428,8 @@ FontFaceLayer* FontFaceHandleDefault::GenerateLayer(const SharedPtr<const FontEf
 	if (i != layers.end())
 		return i->second.get();
 
-	UniquePtr<FontFaceLayer> layer_ptr = std::make_unique<FontFaceLayer>();
-	FontFaceLayer* layer = layer_ptr.get();
-	layers[font_effect.get()] = std::move(layer_ptr);
+	auto& layer = layers[font_effect.get()];
+	layer = std::make_unique<FontFaceLayer>();
 
 	if (!font_effect)
 	{
@@ -416,31 +439,31 @@ FontFaceLayer* FontFaceHandleDefault::GenerateLayer(const SharedPtr<const FontEf
 	{
 		// Determine which, if any, layer the new layer should copy its geometry and textures from.
 		FontFaceLayer* clone = nullptr;
-		bool deep_clone = true;
+		bool clone_glyph_origins = true;
 		String generation_key;
 		size_t fingerprint = font_effect->GetFingerprint();
 
 		if (!font_effect->HasUniqueTexture())
 		{
 			clone = base_layer;
-			deep_clone = false;
+			clone_glyph_origins = false;
 		}
 		else
 		{
-			FontLayerCache::iterator cache_iterator = layer_cache.find(fingerprint);
+			auto cache_iterator = layer_cache.find(fingerprint);
 			if (cache_iterator != layer_cache.end())
 				clone = cache_iterator->second;
 		}
 
 		// Create a new layer.
-		layer->Initialise(this, font_effect, clone, deep_clone);
+		layer->Initialise(this, font_effect, clone, clone_glyph_origins);
 
 		// Cache the layer in the layer cache if it generated its own textures (ie, didn't clone).
 		if (!clone)
-			layer_cache[fingerprint] = layer;
+			layer_cache[fingerprint] = layer.get();
 	}
 
-	return layer;
+	return layer.get();
 }
 
 #endif

+ 34 - 14
Source/Core/FontFaceLayer.cpp

@@ -30,6 +30,7 @@
 #include "FontFaceLayer.h"
 #include "../../Include/RmlUi/Core/Core.h"
 #include "FontFaceHandleDefault.h"
+#include "FontDatabaseDefault.h"
 
 namespace Rml {
 namespace Core {
@@ -37,37 +38,57 @@ namespace Core {
 #ifndef RMLUI_NO_FONT_INTERFACE_DEFAULT
 
 FontFaceLayer::FontFaceLayer() : colour(255, 255, 255)
-{
-	handle = nullptr;
-}
+{}
 
 FontFaceLayer::~FontFaceLayer()
 {
 }
 
 // Generates the character and texture data for the layer.
-bool FontFaceLayer::Initialise(const FontFaceHandleDefault* _handle, SharedPtr<const FontEffect> _effect, const FontFaceLayer* clone, bool deep_clone)
+bool FontFaceLayer::Initialise(const FontFaceHandleDefault* handle, SharedPtr<const FontEffect> _effect, const FontFaceLayer* clone, bool clone_glyph_origins)
 {
-	handle = _handle;
 	effect = _effect;
 	if (effect)
 		colour = effect->GetColour();
 
+	bool result = GenerateLayout(handle, clone, clone_glyph_origins);
+
+	return result;
+}
+
+bool FontFaceLayer::Regenerate(const FontFaceHandleDefault* handle, const FontFaceLayer* clone, bool clone_glyph_origins)
+{
+	// @performance: We could be much smarter about this, e.g. such as adding new glyphs to the existing texture layout and textures.
+	// Right now we re-generate the whole thing, including textures.
+
+	for (auto& texture : textures)
+		texture.RemoveDatabaseCache();
+
+	texture_layout = TextureLayout{};
+	characters.clear();
+	textures.clear();
+
+	bool result = GenerateLayout(handle, clone, clone_glyph_origins);
+
+	return result;
+}
+
+bool FontFaceLayer::GenerateLayout(const FontFaceHandleDefault* handle, const FontFaceLayer* clone, bool clone_glyph_origins)
+{
 	const FontGlyphMap& glyphs = handle->GetGlyphs();
 
 	// Clone the geometry and textures from the clone layer.
-	if (clone != nullptr)
+	if (clone)
 	{
 		// Copy the cloned layer's characters.
 		characters = clone->characters;
 
-		// Copy (and reference) the cloned layer's textures.
+		// Copy the cloned layer's textures.
 		for (size_t i = 0; i < clone->textures.size(); ++i)
 			textures.push_back(clone->textures[i]);
 
-		// Request the effect (if we have one) adjust the origins as appropriate.
-		if (!deep_clone &&
-			effect != nullptr)
+		// Request the effect (if we have one) and adjust the origins as appropriate.
+		if (effect && !clone_glyph_origins)
 		{
 			for (auto& pair : glyphs)
 			{
@@ -104,7 +125,7 @@ bool FontFaceLayer::Initialise(const FontFaceHandleDefault* _handle, SharedPtr<c
 			Vector2i glyph_dimensions = glyph.bitmap_dimensions;
 
 			// Adjust glyph origin / dimensions for the font effect.
-			if (effect != nullptr)
+			if (effect)
 			{
 				if (!effect->GetGlyphMetrics(glyph_origin, glyph_dimensions, glyph))
 					continue;
@@ -161,17 +182,16 @@ bool FontFaceLayer::Initialise(const FontFaceHandleDefault* _handle, SharedPtr<c
 
 
 	return true;
+	return false;
 }
 
 // Generates the texture data for a layer (for the texture database).
-bool FontFaceLayer::GenerateTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id)
+bool FontFaceLayer::GenerateTexture(const FontGlyphMap& glyphs, UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id)
 {
 	if (texture_id < 0 ||
 		texture_id > texture_layout.GetNumTextures())
 		return false;
 
-	const FontGlyphMap& glyphs = handle->GetGlyphs();
-
 	// Generate the texture data.
 	texture_data = texture_layout.GetTexture(texture_id).AllocateTexture();
 	texture_dimensions = texture_layout.GetTexture(texture_id).GetDimensions();

+ 10 - 10
Source/Core/FontFaceLayer.h

@@ -60,16 +60,18 @@ public:
 	/// @param[in] handle The handle generating this layer.
 	/// @param[in] effect The effect to initialise the layer with.
 	/// @param[in] clone The layer to optionally clone geometry and texture data from.
-	/// @param[in] deep_clone If true, the clones geometry will be completely cloned and the effect will have no option to affect even the glyph origins.
 	/// @return True if the layer was generated successfully, false if not.
-	bool Initialise(const FontFaceHandleDefault* handle, SharedPtr<const FontEffect> effect = {}, const FontFaceLayer* clone = nullptr, bool deep_clone = false);
+	bool Initialise(const FontFaceHandleDefault* handle, SharedPtr<const FontEffect> effect = {}, const FontFaceLayer* clone = nullptr, bool clone_glyph_origins = false);
+
+	/// Clears and re-initalize the font face layer, using the font effect it was first initialized with, such as to add new glyphs to the layer.
+	bool Regenerate(const FontFaceHandleDefault* handle, const FontFaceLayer* clone = nullptr, bool clone_glyph_origins = false);
 
 	/// Generates the texture data for a layer (for the texture database).
 	/// @param[out] texture_data The pointer to be set to the generated texture data.
 	/// @param[out] texture_dimensions The dimensions of the texture.
 	/// @param[in] glyphs The glyphs required by the font face handle.
 	/// @param[in] texture_id The index of the texture within the layer to generate.
-	bool GenerateTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id);
+	bool GenerateTexture(const FontGlyphMap& glyphs, UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id);
 	/// Generates the geometry required to render a single character.
 	/// @param[out] geometry An array of geometries this layer will write to. It must be at least as big as the number of textures in this layer.
 	/// @param[in] character_code The character to generate geometry for.
@@ -105,22 +107,21 @@ public:
 	}
 
 	/// Returns the effect used to generate the layer.
-	/// @return The layer's effect.
 	const FontEffect* GetFontEffect() const;
 
-	/// Returns on the layer's textures.
-	/// @param[in] index The index of the desired texture.
-	/// @return The requested texture.
+	/// Returns one of the layer's textures.
 	const Texture* GetTexture(int index);
 	/// Returns the number of textures employed by this layer.
-	/// @return The number of used textures.
 	int GetNumTextures() const;
 
 	/// Returns the layer's colour.
-	/// @return The layer's colour.
 	const Colourb& GetColour() const;
 
 private:
+
+	bool GenerateLayout(const FontFaceHandleDefault* handle, const FontFaceLayer* clone, bool clone_glyph_origins);
+
+
 	struct Character
 	{
 		Character() : texture_index(-1) { }
@@ -139,7 +140,6 @@ private:
 	using CharacterMap = UnorderedMap<CodePoint, Character>;
 	using TextureList = std::vector<Texture>;
 
-	const FontFaceHandleDefault* handle;
 	SharedPtr<const FontEffect> effect;
 
 	TextureLayout texture_layout;

+ 5 - 0
Source/Core/Texture.cpp

@@ -69,6 +69,11 @@ Vector2i Texture::GetDimensions(RenderInterface* render_interface) const
 	return resource->GetDimensions(render_interface);
 }
 
+void Texture::RemoveDatabaseCache() const
+{
+	TextureDatabase::RemoveTexture(resource.get());
+}
+
 bool Texture::operator==(const Texture& other) const
 {
 	return resource == other.resource;