Browse Source

Render replacement character for unknown characters

Michael Ragazzon 6 years ago
parent
commit
3998dd1f3a

+ 1 - 1
Include/RmlUi/Core/Types.h

@@ -60,7 +60,7 @@ namespace Core {
 typedef unsigned char byte;
 typedef double Time;
 typedef void* ScriptObject;
-enum class CodePoint : unsigned int { Null };
+enum class CodePoint : unsigned int { Null, Replacement = 0xfffd };
 
 }
 }

+ 2 - 0
Samples/basic/demo/data/demo.rml

@@ -124,6 +124,8 @@ button:focus {
 
 <div style="font-size: 0.85em; text-align: left;" id="fps"></div>
 
+<p><br/><br/>Strings: abc (æøå) def</p>
+
 <tabset id="menu">
 <tab>Decorators</tab>
 <panel id="decorators">

+ 12 - 2
Source/Core/FontFaceHandle.cpp

@@ -94,7 +94,12 @@ int FontFaceHandle::GetStringWidth(const String& string, CodePoint prior_charact
 
 		auto it_glyph = glyphs.find(code_point);
 		if (it_glyph == glyphs.end())
-			continue;
+		{
+			code_point = CodePoint::Replacement;
+			it_glyph = glyphs.find(code_point);
+			if (it_glyph == glyphs.end())
+				continue;
+		}
 
 		const FontGlyph& glyph = it_glyph->second;
 
@@ -226,7 +231,12 @@ int FontFaceHandle::GenerateString(GeometryList& geometry, const String& string,
 
 			auto it_glyph = glyphs.find(code_point);
 			if (it_glyph == glyphs.end())
-				continue;
+			{
+				code_point = CodePoint::Replacement;
+				it_glyph = glyphs.find(code_point);
+				if (it_glyph == glyphs.end())
+					continue;
+			}
 
 			const FontGlyph& glyph = it_glyph->second;
 

+ 3 - 1
Source/Core/FontFaceLayer.cpp

@@ -117,9 +117,11 @@ bool FontFaceLayer::Initialise(const FontFaceHandle* _handle, SharedPtr<const Fo
 			texture_layout.AddRectangle((int)code_point, glyph_dimensions - glyph_origin);
 		}
 
+		constexpr int max_texture_dimensions = 1024;
+
 		// Generate the texture layout; this will position the glyph rectangles efficiently and
 		// allocate the texture data ready for writing.
-		if (!texture_layout.GenerateLayout(512))
+		if (!texture_layout.GenerateLayout(max_texture_dimensions))
 			return false;
 
 

+ 38 - 5
Source/Core/FreeType/FontFaceHandle.cpp

@@ -37,7 +37,7 @@ namespace Rml {
 namespace Core {
 
 static FontGlyph BuildGlyph(FT_GlyphSlot ft_glyph);
-static void BuildGlyphMap(FT_Face ft_face, FontGlyphMap& glyphs);
+static void BuildGlyphMap(FT_Face ft_face, FontGlyphMap& glyphs, int size);
 static void GenerateMetrics(FT_Face ft_face, const FontGlyphMap& glyphs, FontMetrics& metrics);
 
 
@@ -66,7 +66,7 @@ bool FontFaceHandle_FreeType::Initialise(FT_Face ft_face, int size)
 	}
 
 	// Construct the initial list of glyphs.
-	BuildGlyphMap(ft_face, GetGlyphs());
+	BuildGlyphMap(ft_face, GetGlyphs(), size);
 
 	// Generate the metrics for the handle.
 	GenerateMetrics(ft_face, GetGlyphs(), GetMetrics());
@@ -77,14 +77,20 @@ bool FontFaceHandle_FreeType::Initialise(FT_Face ft_face, int size)
 	return true;
 }
 
-static void BuildGlyphMap(FT_Face ft_face, FontGlyphMap& glyphs)
+static void BuildGlyphMap(FT_Face ft_face, FontGlyphMap& glyphs, int size)
 {
 	// TODO: ASCII range for now
 	FT_ULong code_min = 32;
 	FT_ULong code_max = 126;
 
+	glyphs.reserve(code_max - code_min + 2);
+
 	for (FT_ULong character_code = code_min; character_code <= code_max; ++character_code)
 	{
+		// Add 'ø' character for testing. TODO: Remove!
+		if (character_code == 126)
+			character_code = 0xf8;
+
 		int index = FT_Get_Char_Index(ft_face, character_code);
 		if (index != 0)
 		{
@@ -105,6 +111,33 @@ static void BuildGlyphMap(FT_Face ft_face, FontGlyphMap& glyphs)
 			glyphs[(CodePoint)character_code] = BuildGlyph(ft_face->glyph);
 		}
 	}
+
+	// Add a replacement character for rendering unknown characters.
+	CodePoint replacement_character = CodePoint::Replacement;
+	auto it = glyphs.find(replacement_character);
+	if(it == glyphs.end())
+	{
+		FontGlyph glyph;
+		glyph.dimensions = { size / 3, (size * 2) / 3 };
+		glyph.bitmap_dimensions = glyph.dimensions;
+		glyph.advance = glyph.dimensions.x + 2;
+		glyph.bearing = { 1, glyph.dimensions.y };
+
+		glyph.bitmap_data.reset(new byte[glyph.bitmap_dimensions.x * glyph.bitmap_dimensions.y]);
+
+		for (int y = 0; y < glyph.bitmap_dimensions.y; y++)
+		{
+			for (int x = 0; x < glyph.bitmap_dimensions.x; x++)
+			{
+				constexpr int stroke = 1;
+				int i = y * glyph.bitmap_dimensions.x + x;
+				bool near_edge = (x < stroke || x >= glyph.bitmap_dimensions.x - stroke || y < stroke || y >= glyph.bitmap_dimensions.y - stroke);
+				glyph.bitmap_data[i] = (near_edge ? 0xdd : 0);
+			}
+		}
+
+		glyphs[replacement_character] = std::move(glyph);
+	}
 }
 
 static FontGlyph BuildGlyph(FT_GlyphSlot ft_glyph)
@@ -140,7 +173,7 @@ static FontGlyph BuildGlyph(FT_GlyphSlot ft_glyph)
 		{
 			glyph.bitmap_data.reset(new byte[glyph.bitmap_dimensions.x * glyph.bitmap_dimensions.y]);
 
-			byte* source_bitmap = ft_glyph->bitmap.buffer;
+			const byte* source_bitmap = ft_glyph->bitmap.buffer;
 			byte* destination_bitmap = glyph.bitmap_data.get();
 
 			// Copy the bitmap data into the newly-allocated space on our glyph.
@@ -152,7 +185,7 @@ static FontGlyph BuildGlyph(FT_GlyphSlot ft_glyph)
 				for (int i = 0; i < glyph.bitmap_dimensions.y; ++i)
 				{
 					int mask = 0x80;
-					byte* source_byte = source_bitmap;
+					const byte* source_byte = source_bitmap;
 					for (int j = 0; j < glyph.bitmap_dimensions.x; ++j)
 					{
 						if ((*source_byte & mask) == mask)

+ 1 - 0
Source/Core/Geometry.cpp

@@ -84,6 +84,7 @@ void Geometry::Render(const Vector2f& translation)
 	// Render our compiled geometry if possible.
 	if (compiled_geometry)
 	{
+		// TODO: We may need to update the compiled geometry somehow when we update our texture.
 		RMLUI_ZoneScopedN("RenderCompiled");
 		render_interface->RenderCompiledGeometry(compiled_geometry, translation);
 	}

+ 1 - 0
Source/Core/TextureLayoutTexture.cpp

@@ -73,6 +73,7 @@ int TextureLayoutTexture::Generate(TextureLayout& layout, int maximum_dimensions
 	}
 
 	int texture_width = Math::RealToInteger(Math::SquareRoot((float) square_pixels));
+
 	dimensions.y = Math::ToPowerOfTwo(texture_width);
 	dimensions.x = dimensions.y >> 1;