|
|
@@ -29,6 +29,7 @@
|
|
|
#include "FontFaceLayer.h"
|
|
|
#include "FontFaceHandleHarfBuzz.h"
|
|
|
#include <string.h>
|
|
|
+#include <type_traits>
|
|
|
|
|
|
FontFaceLayer::FontFaceLayer(const SharedPtr<const FontEffect>& _effect) : colour(255, 255, 255)
|
|
|
{
|
|
|
@@ -52,6 +53,7 @@ bool FontFaceLayer::Generate(const FontFaceHandleHarfBuzz* handle, const FontFac
|
|
|
}
|
|
|
|
|
|
const FontGlyphMap& glyphs = handle->GetGlyphs();
|
|
|
+ const FallbackFontGlyphMap& fallback_glyphs = handle->GetFallbackGlyphs();
|
|
|
|
|
|
// Generate the new layout.
|
|
|
if (clone)
|
|
|
@@ -68,57 +70,40 @@ bool FontFaceLayer::Generate(const FontFaceHandleHarfBuzz* handle, const FontFac
|
|
|
for (auto& pair : glyphs)
|
|
|
{
|
|
|
FontGlyphIndex glyph_index = pair.first;
|
|
|
- const FontGlyph& glyph = pair.second;
|
|
|
+ const FontGlyph& glyph = pair.second.bitmap;
|
|
|
+ const Character glyph_character = pair.second.character;
|
|
|
|
|
|
- auto it = character_boxes.find(glyph_index);
|
|
|
- if (it == character_boxes.end())
|
|
|
- {
|
|
|
- // This can happen if the layers have been dirtied in FontHandleDefault. We will
|
|
|
- // probably be regenerated soon, just skip the character for now.
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- TextureBox& box = it->second;
|
|
|
+ CloneTextureBox(glyph, glyph_index, glyph_character);
|
|
|
+ }
|
|
|
|
|
|
- Vector2i glyph_origin = Vector2i(box.origin);
|
|
|
- Vector2i glyph_dimensions = Vector2i(box.dimensions);
|
|
|
+ for (auto& pair : fallback_glyphs)
|
|
|
+ {
|
|
|
+ const Character glyph_character = pair.first;
|
|
|
+ const FontGlyph& glyph = pair.second;
|
|
|
|
|
|
- if (effect->GetGlyphMetrics(glyph_origin, glyph_dimensions, glyph))
|
|
|
- box.origin = Vector2f(glyph_origin);
|
|
|
- else
|
|
|
- box.texture_index = -1;
|
|
|
+ CloneTextureBox(glyph, 0, glyph_character);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// Initialise the texture layout for the glyphs.
|
|
|
- character_boxes.reserve(glyphs.size());
|
|
|
+ character_boxes.reserve(glyphs.size() + fallback_glyphs.size());
|
|
|
for (auto& pair : glyphs)
|
|
|
{
|
|
|
FontGlyphIndex glyph_index = pair.first;
|
|
|
- const FontGlyph& glyph = pair.second;
|
|
|
-
|
|
|
- Vector2i glyph_origin(0, 0);
|
|
|
- Vector2i glyph_dimensions = glyph.bitmap_dimensions;
|
|
|
-
|
|
|
- // Adjust glyph origin / dimensions for the font effect.
|
|
|
- if (effect)
|
|
|
- {
|
|
|
- if (!effect->GetGlyphMetrics(glyph_origin, glyph_dimensions, glyph))
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- TextureBox box;
|
|
|
- box.origin = Vector2f(float(glyph_origin.x + glyph.bearing.x), float(glyph_origin.y - glyph.bearing.y));
|
|
|
- box.dimensions = Vector2f(glyph_dimensions);
|
|
|
+ const FontGlyph& glyph = pair.second.bitmap;
|
|
|
+ Character glyph_character = pair.second.character;
|
|
|
|
|
|
- RMLUI_ASSERT(box.dimensions.x >= 0 && box.dimensions.y >= 0);
|
|
|
+ CreateTextureLayout(glyph, glyph_index, glyph_character);
|
|
|
+ }
|
|
|
|
|
|
- character_boxes[glyph_index] = box;
|
|
|
+ for (auto& pair : fallback_glyphs)
|
|
|
+ {
|
|
|
+ Character glyph_character = pair.first;
|
|
|
+ const FontGlyph& glyph = pair.second;
|
|
|
|
|
|
- // Add the character's dimensions into the texture layout engine.
|
|
|
- texture_layout.AddRectangle((int)glyph_index, glyph_dimensions);
|
|
|
+ CreateTextureLayout(glyph, 0, glyph_character);
|
|
|
}
|
|
|
|
|
|
constexpr int max_texture_dimensions = 1024;
|
|
|
@@ -132,11 +117,11 @@ bool FontFaceLayer::Generate(const FontFaceHandleHarfBuzz* handle, const FontFac
|
|
|
// appropriate and generating geometry.
|
|
|
for (int i = 0; i < texture_layout.GetNumRectangles(); ++i)
|
|
|
{
|
|
|
- Rml::TextureLayoutRectangle& rectangle = texture_layout.GetRectangle(i);
|
|
|
- const Rml::TextureLayoutTexture& texture = texture_layout.GetTexture(rectangle.GetTextureIndex());
|
|
|
- FontGlyphIndex glyph_index = (FontGlyphIndex)rectangle.GetId();
|
|
|
- RMLUI_ASSERT(character_boxes.find(glyph_index) != character_boxes.end());
|
|
|
- TextureBox& box = character_boxes[glyph_index];
|
|
|
+ TextureLayoutRectangle& rectangle = texture_layout.GetRectangle(i);
|
|
|
+ const TextureLayoutTexture& texture = texture_layout.GetTexture(rectangle.GetTextureIndex());
|
|
|
+ uint64_t font_glyph_id = rectangle.GetId();
|
|
|
+ RMLUI_ASSERT(character_boxes.find(font_glyph_id) != character_boxes.end());
|
|
|
+ TextureBox& box = character_boxes[font_glyph_id];
|
|
|
|
|
|
// Set the character's texture index.
|
|
|
box.texture_index = rectangle.GetTextureIndex();
|
|
|
@@ -177,7 +162,8 @@ bool FontFaceLayer::Generate(const FontFaceHandleHarfBuzz* handle, const FontFac
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-bool FontFaceLayer::GenerateTexture(Vector<byte>& texture_data, Vector2i& texture_dimensions, int texture_id, const FontGlyphMap& glyphs)
|
|
|
+bool FontFaceLayer::GenerateTexture(Vector<byte>& texture_data, Vector2i& texture_dimensions, int texture_id, const FontGlyphMap& glyphs,
|
|
|
+ const FallbackFontGlyphMap& fallback_glyphs)
|
|
|
{
|
|
|
if (texture_id < 0 || texture_id > texture_layout.GetNumTextures())
|
|
|
return false;
|
|
|
@@ -188,33 +174,52 @@ bool FontFaceLayer::GenerateTexture(Vector<byte>& texture_data, Vector2i& textur
|
|
|
|
|
|
for (int i = 0; i < texture_layout.GetNumRectangles(); ++i)
|
|
|
{
|
|
|
- Rml::TextureLayoutRectangle& rectangle = texture_layout.GetRectangle(i);
|
|
|
- FontGlyphIndex glyph_index = (FontGlyphIndex)rectangle.GetId();
|
|
|
- RMLUI_ASSERT(character_boxes.find(glyph_index) != character_boxes.end());
|
|
|
+ TextureLayoutRectangle& rectangle = texture_layout.GetRectangle(i);
|
|
|
+ uint64_t font_glyph_id = rectangle.GetId();
|
|
|
+ RMLUI_ASSERT(character_boxes.find(font_glyph_id) != character_boxes.end());
|
|
|
|
|
|
- TextureBox& box = character_boxes[glyph_index];
|
|
|
+ TextureBox& box = character_boxes[font_glyph_id];
|
|
|
|
|
|
if (box.texture_index != texture_id)
|
|
|
continue;
|
|
|
|
|
|
- auto it = glyphs.find((FontGlyphIndex)rectangle.GetId());
|
|
|
- if (it == glyphs.end())
|
|
|
- continue;
|
|
|
+ const FontGlyph* glyph = nullptr;
|
|
|
+ FontGlyphIndex glyph_index = GetFontGlyphIndexFromID(font_glyph_id);
|
|
|
+ Rml::Character glyph_character = GetCharacterCodepointFromID(font_glyph_id);
|
|
|
|
|
|
- const FontGlyph& glyph = it->second;
|
|
|
+ // Get the glyph bitmap by looking it up with the glyph index.
|
|
|
+ auto it = glyphs.find(glyph_index);
|
|
|
+ if (it == glyphs.end() || glyph_index == 0)
|
|
|
+ {
|
|
|
+ // Glyph was not found; attempt to find it in the fallback glyphs.
|
|
|
+ auto fallback_it = fallback_glyphs.find(glyph_character);
|
|
|
+ if (fallback_it == fallback_glyphs.end())
|
|
|
+ if (it != glyphs.end())
|
|
|
+ // Fallback glyph was not found, but replacement glyph bitmap exists, so use it instead.
|
|
|
+ glyph = &it->second.bitmap;
|
|
|
+ else
|
|
|
+ // No fallback glyph nor replacement glyph bitmap was found; ignore this glyph.
|
|
|
+ continue;
|
|
|
+ else
|
|
|
+ // Fallback glyph was found.
|
|
|
+ glyph = &fallback_it->second;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ // Glyph was found.
|
|
|
+ glyph = &it->second.bitmap;
|
|
|
|
|
|
if (effect == nullptr)
|
|
|
{
|
|
|
// Copy the glyph's bitmap data into its allocated texture.
|
|
|
- if (glyph.bitmap_data)
|
|
|
+ if (glyph->bitmap_data)
|
|
|
{
|
|
|
byte* destination = rectangle.GetTextureData();
|
|
|
- const byte* source = glyph.bitmap_data;
|
|
|
- const int num_bytes_per_line = glyph.bitmap_dimensions.x * (glyph.color_format == ColorFormat::RGBA8 ? 4 : 1);
|
|
|
+ const byte* source = glyph->bitmap_data;
|
|
|
+ const int num_bytes_per_line = glyph->bitmap_dimensions.x * (glyph->color_format == ColorFormat::RGBA8 ? 4 : 1);
|
|
|
|
|
|
- for (int j = 0; j < glyph.bitmap_dimensions.y; ++j)
|
|
|
+ for (int j = 0; j < glyph->bitmap_dimensions.y; ++j)
|
|
|
{
|
|
|
- switch (glyph.color_format)
|
|
|
+ switch (glyph->color_format)
|
|
|
{
|
|
|
case ColorFormat::A8:
|
|
|
{
|
|
|
@@ -238,7 +243,7 @@ bool FontFaceLayer::GenerateTexture(Vector<byte>& texture_data, Vector2i& textur
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- effect->GenerateGlyphTexture(rectangle.GetTextureData(), Vector2i(box.dimensions), rectangle.GetTextureStride(), glyph);
|
|
|
+ effect->GenerateGlyphTexture(rectangle.GetTextureData(), Vector2i(box.dimensions), rectangle.GetTextureStride(), *glyph);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -267,3 +272,65 @@ ColourbPremultiplied FontFaceLayer::GetColour(float opacity) const
|
|
|
{
|
|
|
return colour.ToPremultiplied(opacity);
|
|
|
}
|
|
|
+
|
|
|
+uint64_t FontFaceLayer::CreateFontGlyphID(const FontGlyphIndex glyph_index, const Character character_code) const
|
|
|
+{
|
|
|
+ return (static_cast<uint64_t>(glyph_index) << (sizeof(Character) * 8)) | static_cast<uint64_t>(std::underlying_type_t<Character>(character_code));
|
|
|
+}
|
|
|
+
|
|
|
+FontGlyphIndex FontFaceLayer::GetFontGlyphIndexFromID(const uint64_t glyph_id) const
|
|
|
+{
|
|
|
+ return static_cast<FontGlyphIndex>(glyph_id >> (sizeof(Character) * 8));
|
|
|
+}
|
|
|
+
|
|
|
+Character FontFaceLayer::GetCharacterCodepointFromID(const uint64_t glyph_id) const
|
|
|
+{
|
|
|
+ return static_cast<Character>(glyph_id & static_cast<std::underlying_type_t<Character>>(-1));
|
|
|
+}
|
|
|
+
|
|
|
+void FontFaceLayer::CreateTextureLayout(const FontGlyph& glyph, FontGlyphIndex glyph_index, Character glyph_character)
|
|
|
+{
|
|
|
+ Vector2i glyph_origin(0, 0);
|
|
|
+ Vector2i glyph_dimensions = glyph.bitmap_dimensions;
|
|
|
+
|
|
|
+ // Adjust glyph origin / dimensions for the font effect.
|
|
|
+ if (effect)
|
|
|
+ {
|
|
|
+ if (!effect->GetGlyphMetrics(glyph_origin, glyph_dimensions, glyph))
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ TextureBox box;
|
|
|
+ box.origin = Vector2f(float(glyph_origin.x + glyph.bearing.x), float(glyph_origin.y - glyph.bearing.y));
|
|
|
+ box.dimensions = Vector2f(glyph_dimensions);
|
|
|
+
|
|
|
+ RMLUI_ASSERT(box.dimensions.x >= 0 && box.dimensions.y >= 0);
|
|
|
+
|
|
|
+ uint64_t font_glyph_id = CreateFontGlyphID(glyph_index, glyph_character);
|
|
|
+ character_boxes[font_glyph_id] = box;
|
|
|
+
|
|
|
+ // Add the character's dimensions into the texture layout engine.
|
|
|
+ texture_layout.AddRectangle(font_glyph_id, glyph_dimensions);
|
|
|
+}
|
|
|
+
|
|
|
+void FontFaceLayer::CloneTextureBox(const FontGlyph& glyph, FontGlyphIndex glyph_index, Character glyph_character)
|
|
|
+{
|
|
|
+ auto it = character_boxes.find(CreateFontGlyphID(glyph_index, glyph_character));
|
|
|
+ if (it == character_boxes.end())
|
|
|
+ {
|
|
|
+ // This can happen if the layers have been dirtied in FontHandleDefault. We will
|
|
|
+ // probably be regenerated soon, just skip the character for now.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ TextureBox& box = it->second;
|
|
|
+
|
|
|
+ Vector2i glyph_origin = Vector2i(box.origin);
|
|
|
+ Vector2i glyph_dimensions = Vector2i(box.dimensions);
|
|
|
+
|
|
|
+ if (effect->GetGlyphMetrics(glyph_origin, glyph_dimensions, glyph))
|
|
|
+ box.origin = Vector2f(glyph_origin);
|
|
|
+ else
|
|
|
+ box.texture_index = -1;
|
|
|
+
|
|
|
+}
|