Browse Source

Lazy loading unicode characters (work in progress!)

Michael Ragazzon 6 years ago
parent
commit
ac42bcb8ec

+ 10 - 2
Include/RmlUi/Core/FontEngineInterface.h

@@ -51,7 +51,7 @@ public:
 	/// @param[in] family The family of the desired font handle.
 	/// @param[in] style The style of the desired font handle.
 	/// @param[in] weight The weight of the desired font handle.
-	/// @param[in] size The size of desired handle, in points.
+	/// @param[in] size The size of desired handle, in points. // TODO: Pixels?
 	/// @return A valid handle if a matching (or closely matching) font face was found, NULL otherwise.
 	virtual FontFaceHandle GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size);
 
@@ -60,11 +60,13 @@ public:
 	/// @param[in] handle The font handle.
 	/// @param[in] font_effects The list of font effects to generate the configuration for.
 	/// @return The index to use when generating geometry using this configuration.
+	// TODO: Should return a handle.
 	virtual int GenerateLayerConfiguration(FontFaceHandle handle, const FontEffectList &font_effects);
 
 	/// Should return the average advance of all glyphs in this font face.
 	/// @param[in] handle The font handle.
 	/// @return An approximate width of the characters in this font face.
+	// TODO: Not used, remove?
 	virtual int GetCharacterWidth(FontFaceHandle handle);
 
 	/// Should returns the point size of this font face.
@@ -88,9 +90,10 @@ public:
 	/// Should return the font's underline, as a pixel offset from the bottom of the font.
 	/// @param[in] handle The font handle.
 	/// @return The font's underline thickness.
+	// TODO: Thickness vs Return value? Pointer?
 	virtual float GetUnderline(FontFaceHandle handle, float *thickness);
 
-	/// Should return the width of a string when rendered with this handle.
+	/// Called by RmlUi when it wants to retrieve the width of a string when rendered with this handle.
 	/// @param[in] handle The font handle.
 	/// @param[in] string The string to measure.
 	/// @param[in] prior_character The optionally-specified character that immediately precedes the string. This may have an impact on the string width due to kerning.
@@ -106,7 +109,12 @@ public:
 	/// @param[in] colour The colour to render the text.
 	/// @param[in] layer_configuration The layer for which the geometry should be generated.
 	/// @return The width, in pixels, of the string geometry.
+	// TODO: Layer configuration should be a handle. Reorder arguments to take the handles first, geometry last/first.
 	virtual int GenerateString(FontFaceHandle handle, GeometryList& geometry, const String& string, const Vector2f& position, const Colourb& colour, int layer_configuration);
+
+	/// Should return a new value whenever the geometry need to be re-generated.
+	virtual int GetVersion(FontFaceHandle handle);
+
 };
 
 }

+ 6 - 1
Samples/basic/demo/data/demo.rml

@@ -177,7 +177,12 @@ button:focus {
 <tab>Controls</tab>
 <panel>
 	<div>Type something here: <input style="vertical-align: -7px;" size="10" type="text" maxlength="12" value="Sample text"/></div>
-	<textarea cols="30" rows="5" wrap="nowrap">Hello World!</textarea>
+	<textarea cols="30" rows="5" wrap="nowrap">Hello World! dfd dsfds fdsfdf 
+sdfdfds d,. sfdsf. Heøøllo! Who are you? (I don't know),
+maybe there is åsomething å here??? dsfsdfsdf dfsd
+sd sad sads
+
+as</textarea>
 </panel>
 </tabset>
 <div class="left-arrow"/>

+ 9 - 0
Source/Core/ElementTextDefault.cpp

@@ -53,6 +53,7 @@ ElementTextDefault::ElementTextDefault(const String& tag) : ElementText(tag), co
 
 	font_configuration = -1;
 	font_dirty = true;
+	font_handle_version = 0;
 }
 
 ElementTextDefault::~ElementTextDefault()
@@ -88,6 +89,14 @@ void ElementTextDefault::OnRender()
 	if (font_dirty && UpdateFontConfiguration())
 		geometry_dirty = true;
 
+	// Dirty geometry if font version has changed
+	int new_version = GetFontEngineInterface()->GetVersion(font_face_handle);
+	if (new_version != font_handle_version)
+	{
+		font_handle_version = new_version;
+		geometry_dirty = true;
+	}
+
 	// Regenerate the geometry if the colour or font configuration has altered.
 	if (geometry_dirty)
 		GenerateGeometry(font_face_handle);

+ 1 - 0
Source/Core/ElementTextDefault.h

@@ -131,6 +131,7 @@ private:
 	Style::TextDecoration decoration_property;
 
 	int font_configuration;
+	int font_handle_version;
 	bool font_dirty;
 };
 

+ 5 - 0
Source/Core/FontEngineInterface.cpp

@@ -107,5 +107,10 @@ int FontEngineInterface::GenerateString(FontFaceHandle, GeometryList& RMLUI_UNUS
 	return 0;
 }
 
+int FontEngineInterface::GetVersion(FontFaceHandle handle)
+{
+	return 0;
+}
+
 }
 }

+ 6 - 0
Source/Core/FontEngineInterfaceDefault.cpp

@@ -112,6 +112,12 @@ int FontEngineInterfaceDefault::GenerateString(FontFaceHandle handle, GeometryLi
 	return handle_default->GenerateString(geometry, string, position, colour, layer_configuration);
 }
 
+int FontEngineInterfaceDefault::GetVersion(FontFaceHandle handle)
+{
+	auto handle_default = reinterpret_cast<FontFaceHandleDefault*>(handle);
+	return handle_default->UpdateOnDirty();
+}
+
 #endif
 
 }

+ 2 - 0
Source/Core/FontEngineInterfaceDefault.h

@@ -71,6 +71,8 @@ public:
 
 	/// Generates the geometry required to render a single line of text.
 	int GenerateString(FontFaceHandle, GeometryList& geometry, const String& string, const Vector2f& position, const Colourb& colour, int layer_configuration) override;
+
+	int GetVersion(FontFaceHandle handle) override;
 };
 
 }

+ 71 - 32
Source/Core/FontFaceHandleDefault.cpp

@@ -95,33 +95,22 @@ float FontFaceHandleDefault::GetUnderline(float *thickness) const
 }
 
 // Returns the width a string will take up if rendered with this handle.
-int FontFaceHandleDefault::GetStringWidth(const String& string, CodePoint prior_character) const
+int FontFaceHandleDefault::GetStringWidth(const String& string, CodePoint prior_character)
 {
 	int width = 0;
 	for (auto it_string = StringIteratorU8(string); it_string; ++it_string)
 	{
 		CodePoint code_point = *it_string;
 
-		// Don't try to render control characters
-		if ((unsigned int)code_point < (unsigned int)' ')
+		const FontGlyph* glyph = GetOrAppendGlyph(code_point);
+		if (!glyph)
 			continue;
 
-		auto it_glyph = glyphs.find(code_point);
-		if (it_glyph == glyphs.end())
-		{
-			code_point = CodePoint::Replacement;
-			it_glyph = glyphs.find(code_point);
-			if (it_glyph == glyphs.end())
-				continue;
-		}
-
-		const FontGlyph& glyph = it_glyph->second;
-
 		// Adjust the cursor for the kerning between this character and the previous one.
 		if (prior_character != CodePoint::Null)
 			width += GetKerning(prior_character, code_point);
 		// Adjust the cursor for this character's advance.
-		width += glyph.advance;
+		width += glyph->advance;
 
 		prior_character = code_point;
 	}
@@ -203,7 +192,7 @@ bool FontFaceHandleDefault::GenerateLayerTexture(UniquePtr<const byte[]>& textur
 }
 
 // Generates the geometry required to render a single line of text.
-int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String& string, const Vector2f& position, const Colourb& colour, int layer_configuration_index) const
+int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String& string, const Vector2f& position, const Colourb& colour, int layer_configuration_index)
 {
 	int geometry_index = 0;
 	int line_width = 0;
@@ -243,28 +232,17 @@ int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String&
 		{
 			CodePoint code_point = *it_string;
 
-			// Don't try to render control characters
-			if ((unsigned int)code_point < (unsigned int)' ')
+			const FontGlyph* glyph = GetOrAppendGlyph(code_point);
+			if (!glyph)
 				continue;
 
-			auto it_glyph = glyphs.find(code_point);
-			if (it_glyph == glyphs.end())
-			{
-				code_point = CodePoint::Replacement;
-				it_glyph = glyphs.find(code_point);
-				if (it_glyph == glyphs.end())
-					continue;
-			}
-
-			const FontGlyph& glyph = it_glyph->second;
-
 			// Adjust the cursor for the kerning between this character and the previous one.
 			if (prior_character != CodePoint::Null)
 				line_width += GetKerning(prior_character, code_point);
 
 			layer->GenerateGeometry(&geometry[geometry_index], code_point, Vector2f(position.x + line_width, position.y), layer_colour);
 
-			line_width += glyph.advance;
+			line_width += glyph->advance;
 			prior_character = code_point;
 		}
 
@@ -277,6 +255,32 @@ int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String&
 	return line_width;
 }
 
+int FontFaceHandleDefault::UpdateOnDirty()
+{
+	if(is_dirty)
+	{
+		is_dirty = false;
+		++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
+			layers.erase(nullptr);
+			base_layer = GenerateLayer(nullptr);
+			layer_configurations[0][0] = base_layer;
+		}
+	}
+
+	return version;
+}
+
+int FontFaceHandleDefault::GetVersion() const 
+{
+	return version;
+}
+
 FontGlyphMap& FontFaceHandleDefault::GetGlyphs() {
 	return glyphs;
 }
@@ -287,9 +291,44 @@ FontMetrics& FontFaceHandleDefault::GetMetrics() {
 
 void FontFaceHandleDefault::GenerateBaseLayer()
 {
+	RMLUI_ASSERTMSG(layer_configurations.empty(), "This should only be called before any layers are generated.");
 	base_layer = GenerateLayer(nullptr);
-	layer_configurations.push_back(LayerConfiguration());
-	layer_configurations.back().push_back(base_layer);
+	layer_configurations.push_back(LayerConfiguration{ base_layer });
+}
+
+const FontGlyph* FontFaceHandleDefault::GetOrAppendGlyph(CodePoint& code_point)
+{
+	// Don't try to render control characters
+	if ((unsigned int)code_point < (unsigned int)' ')
+		return nullptr;
+
+	auto it_glyph = glyphs.find(code_point);
+	if (it_glyph == glyphs.end())
+	{
+		bool result = AppendGlyph(code_point);
+
+		if (result)
+		{
+			it_glyph = glyphs.find(code_point);
+			if (it_glyph == glyphs.end())
+			{
+				RMLUI_ERROR;
+				return nullptr;
+			}
+
+			is_dirty = true;
+		}
+		else
+		{
+			code_point = CodePoint::Replacement;
+			it_glyph = glyphs.find(code_point);
+			if (it_glyph == glyphs.end())
+				return nullptr;
+		}
+	}
+
+	const FontGlyph* glyph = &it_glyph->second;
+	return glyph;
 }
 
 // Generates (or shares) a layer derived from a font effect.

+ 20 - 6
Source/Core/FontFaceHandleDefault.h

@@ -95,7 +95,7 @@ public:
 	/// @param[in] string The string to measure.
 	/// @param[in] prior_character The optionally-specified character that immediately precedes the string. This may have an impact on the string width due to kerning.
 	/// @return The width, in pixels, this string will occupy if rendered with this handle.
-	int GetStringWidth(const String& string, CodePoint prior_character = CodePoint::Null) const;
+	int GetStringWidth(const String& string, CodePoint prior_character = CodePoint::Null);
 
 	/// Generates, if required, the layer configuration for a given array of font effects.
 	/// @param[in] font_effects The list of font effects to generate the configuration for.
@@ -114,7 +114,12 @@ public:
 	/// @param[in] position The position of the baseline of the first character to render.
 	/// @param[in] colour The colour to render the text.
 	/// @return The width, in pixels, of the string geometry.
-	int GenerateString(GeometryList& geometry, const String& string, const Vector2f& position, const Colourb& colour, int layer_configuration = 0) const;
+	int GenerateString(GeometryList& geometry, const String& string, const Vector2f& position, const Colourb& colour, int layer_configuration = 0);
+
+	// Returns version
+	int UpdateOnDirty();
+
+	int GetVersion() const;
 
 protected:
 
@@ -123,18 +128,24 @@ protected:
 
 	void GenerateBaseLayer();
 
+	// Build and append glyph to 'glyphs'
+	virtual bool AppendGlyph(CodePoint code_point) = 0;
+
 	virtual int GetKerning(CodePoint lhs, CodePoint rhs) const = 0;
 
 private:
 
+	// Note: can modify code_point to change character to e.g. replacement character.
+	const FontGlyph* GetOrAppendGlyph(CodePoint& code_point);
+
 	FontFaceLayer* GenerateLayer(const SharedPtr<const FontEffect>& font_effect);
 
 	FontGlyphMap glyphs;
 
-	typedef SmallUnorderedMap< const FontEffect*, UniquePtr<FontFaceLayer> > FontLayerMap;
-	typedef SmallUnorderedMap< size_t, FontFaceLayer* > FontLayerCache;
-	typedef std::vector< FontFaceLayer* > LayerConfiguration;
-	typedef std::vector< LayerConfiguration > LayerConfigurationList;
+	using FontLayerMap = SmallUnorderedMap< const FontEffect*, UniquePtr<FontFaceLayer> >;
+	using FontLayerCache = SmallUnorderedMap< size_t, FontFaceLayer* >;
+	using LayerConfiguration = std::vector< FontFaceLayer* >;
+	using LayerConfigurationList = std::vector< LayerConfiguration >;
 
 	// The list of all font layers, index by the effect that instanced them.
 	FontFaceLayer* base_layer;
@@ -142,6 +153,9 @@ private:
 	// Each font layer that generated geometry or textures, indexed by the respective generation key.
 	FontLayerCache layer_cache;
 
+	int version = 0;
+	bool is_dirty = false;
+
 	// All configurations currently in use on this handle. New configurations will be generated as required.
 	LayerConfigurationList layer_configurations;
 

+ 1 - 1
Source/Core/FontFaceLayer.cpp

@@ -152,7 +152,7 @@ bool FontFaceLayer::Initialise(const FontFaceHandleDefault* _handle, SharedPtr<c
 		for (int i = 0; i < texture_layout.GetNumTextures(); ++i)
 		{
 			Texture texture;
-			if (!texture.Load(CreateString(64, "?font::%p/%p/%d", handle, effect.get(), i)))
+			if (!texture.Load(CreateString(64, "?font::%p/%p/%d/%d", handle, effect.get(), i, handle->GetVersion())))
 				return false;
 
 			textures.push_back(texture);

+ 20 - 0
Source/Core/FreeType/FontFaceHandle.cpp

@@ -80,6 +80,26 @@ bool FontFaceHandle_FreeType::Initialise(FT_Face ft_face, int size)
 	return true;
 }
 
+bool FontFaceHandle_FreeType::AppendGlyph(CodePoint code_point)
+{
+	FontGlyphMap& glyphs = GetGlyphs();
+	RMLUI_ASSERT(glyphs.find(code_point) == glyphs.end());
+	RMLUI_ASSERT(ft_face);
+
+	// TODO: Why is this needed ??
+	FT_Error error = FT_Set_Char_Size(ft_face, 0, GetMetrics().size << 6, 0, 0);
+	if (error != 0)
+	{
+		Log::Message(Log::LT_ERROR, "Unable to set the character size '%d' on the font face '%s %s'.", GetMetrics().size, ft_face->family_name, ft_face->style_name);
+		return false;
+	}
+
+	if (!BuildGlyph(ft_face, code_point, glyphs))
+		return false;
+
+	return true;
+}
+
 
 static void BuildGlyphMap(FT_Face ft_face, FontGlyphMap& glyphs, int size)
 {

+ 3 - 0
Source/Core/FreeType/FontFaceHandle.h

@@ -59,6 +59,9 @@ public:
 	bool Initialise(FT_Face ft_face, int size);
 
 private:
+	bool AppendGlyph(CodePoint code_point) override;
+
+
 	int GetKerning(CodePoint lhs, CodePoint rhs) const override;
 
 	FT_Face ft_face;

+ 1 - 1
Source/Core/StringUtilities.cpp

@@ -608,7 +608,7 @@ bool StringView::operator==(const StringView& other) const {
 }
 
 
-
+// TODO: Remove seek on construction
 StringIteratorU8::StringIteratorU8(const char* p_begin, const char* p, const char* p_end) : view(p_begin, p_end), p(p) 
 {
 	SeekForward();

+ 10 - 7
Source/Core/TextureResource.cpp

@@ -15,7 +15,7 @@
  *
  * 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
@@ -135,13 +135,16 @@ bool TextureResource::Load(RenderInterface* render_interface)
 			FontFaceHandleDefault* handle;
 			FontEffect* layer_id;
 			int texture_id;
-			
-			if (sscanf(source.c_str(), "?font::%p/%p/%d", &handle, &layer_id, &texture_id) == 3)
+			int handle_version;
+
+			if (sscanf(source.c_str(), "?font::%p/%p/%d/%d", &handle, &layer_id, &texture_id, &handle_version) == 4)
 			{
-				handle->GenerateLayerTexture(data,
-											 dimensions,
-											 layer_id,
-											 texture_id);
+				handle->GenerateLayerTexture(
+					data,
+					dimensions,
+					layer_id,
+					texture_id
+				);
 			}
 		}
 #endif

+ 2 - 1
readme.md

@@ -312,9 +312,10 @@ Two new CMake options added.
 ### Other changes
 
 - `Context::ProcessMouseWheel` now takes a float value for the `wheel_delta` property, thereby enabling continuous/smooth scrolling for input devices with such support. The default scroll length for unity value of `wheel_delta` is now three times the default line-height multiplied by the current dp-ratio.
-- The system interface now has two new functions for setting and getting text to and from the clipboard: `virtual void SystemInterface::SetClipboardText(const Core::WString& text)` and `virtual void SystemInterface::GetClipboardText(Core::WString& text)`.
+- The system interface now has two new functions for setting and getting text to and from the clipboard: `virtual void SystemInterface::SetClipboardText(const Core::String& text)` and `virtual void SystemInterface::GetClipboardText(Core::String& text)`.
 - The debugger now has the ability to clear the log. Additionally, the displayed element information updates when the element changes.
 - The `text-decoration` property can now also be used with `overline` and `line-through`.
+- The text input and text area elements can be navigated word for word by holding the 'ctrl' key.
 
 
 ### Breaking changes