Updated rmlui to master

This commit is contained in:
thecozies 2024-04-20 10:22:46 -05:00
parent 95ab0074ea
commit bf167f31c7
26 changed files with 32 additions and 3070 deletions

View File

@ -30,7 +30,7 @@ set(BUILD_SHARED_LIBS OFF)
SET(LUNASVG_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/lunasvg)
# set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}")
SET(ENABLE_SVG_PLUGIN OFF CACHE BOOL "" FORCE)
SET(ENABLE_SVG_PLUGIN ON CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/RmlUi)
target_include_directories(rt64 PRIVATE ${CMAKE_BINARY_DIR}/rt64/src)
@ -144,17 +144,7 @@ set (SOURCES
${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontEngineInterfaceScaled.cpp
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontFace.cpp
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontFaceHandleScaled.cpp
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontFaceLayer.cpp
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontFamily.cpp
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontProvider.cpp
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FreeTypeInterface.cpp
${CMAKE_SOURCE_DIR}/src/ui/ScaledSVG/ElementScaledSVG.cpp
${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp
${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.cpp

View File

@ -101,15 +101,16 @@
<label><span style="font-family:promptfont;">&#x21A7;</span> Accept</label> -->
</div>
</div>
<div
data-alias-promptOpen="1"
<!-- <div
data-if="1==0"
data-alias-promptOpen="0"
data-alias-promptHeader="'test header'"
data-alias-promptContent="'This allows templates to be used as reusable components within data models. By wrapping the inline template in an element that defines variable name aliases, the template can refer to any outside variable by a fixed name.'"
data-alias-promptConfirmLabel="'Confirm'"
data-alias-promptCancelLabel="'Cancel'"
>
<template src="prompt"/>
</div>
</div> -->
</div>
<!-- </handle> -->
<!-- <handle size_target="#document" style="position: absolute; width: 16dp; height: 16dp; bottom: 0px; right: 0px; cursor: resize;"></handle> -->

View File

@ -2,12 +2,12 @@
<head>
</head>
<body>
<form class="config__form">
<div class="config__hz-wrapper">
<form class="config__form" id="conf-general__form">
<div class="config__hz-wrapper" id="conf-general__hz-wrapper">
<!-- Options -->
<div class="config__wrapper" data-event-mouseout="set_cur_config_index(-1)">
<div class="config__wrapper" data-event-mouseout="set_cur_config_index(-1)" id="conf-general__wrapper">
<!-- targeting mode -->
<div class="config-option" data-event-mouseover="set_cur_config_index(0)">
<div class="config-option" data-event-mouseover="set_cur_config_index(0)" id="conf-general__Targeting-Mode">
<label class="config-option__title">Targeting Mode</label>
<div class="config-option__list">
<input

File diff suppressed because one or more lines are too long

View File

@ -24,6 +24,8 @@
padding: space(16);
text-align: left;
background-color: $color-bg-shadow;
width: auto;
height: auto;
p {
@extend %body;
@ -42,6 +44,7 @@
flex-direction: row;
border-radius: 0dp;
flex: 1 1 100%;
width: 100%;
height: 100%;
text-align: left;
}

@ -1 +1 @@
Subproject commit bb25b881791bc77805b30b22fea01ae42b619bd7
Subproject commit a893ea6386e0c842f90a726a53c9b9e888797519

@ -1 +1 @@
Subproject commit 73cc40b482d0adad226ad101bff40d8ffa69ffeb
Subproject commit 610b8bf5148a27489b4e3344b4f5617b81be38c7

View File

@ -1,100 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#include "FontEngineInterfaceScaled.h"
#include "FontFaceHandleScaled.h"
#include "FontProvider.h"
namespace RecompRml {
using namespace Rml;
FontEngineInterfaceScaled::FontEngineInterfaceScaled()
{
FontProvider::Initialise();
}
FontEngineInterfaceScaled::~FontEngineInterfaceScaled()
{
FontProvider::Shutdown();
}
bool FontEngineInterfaceScaled::LoadFontFace(const String& file_name, bool fallback_face, Style::FontWeight weight)
{
return FontProvider::LoadFontFace(file_name, fallback_face, weight);
}
bool FontEngineInterfaceScaled::LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style,
Style::FontWeight weight, bool fallback_face)
{
return FontProvider::LoadFontFace(data, data_size, font_family, style, weight, fallback_face);
}
FontFaceHandle FontEngineInterfaceScaled::GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size)
{
auto handle = FontProvider::GetFontFaceHandle(family, style, weight, size);
return reinterpret_cast<FontFaceHandle>(handle);
}
FontEffectsHandle FontEngineInterfaceScaled::PrepareFontEffects(FontFaceHandle handle, const FontEffectList& font_effects)
{
auto handle_scaled = reinterpret_cast<FontFaceHandleScaled*>(handle);
return (FontEffectsHandle)handle_scaled->GenerateLayerConfiguration(font_effects);
}
const FontMetrics& FontEngineInterfaceScaled::GetFontMetrics(FontFaceHandle handle)
{
auto handle_scaled = reinterpret_cast<FontFaceHandleScaled*>(handle);
return handle_scaled->GetFontMetrics();
}
int FontEngineInterfaceScaled::GetStringWidth(FontFaceHandle handle, const String& string, float letter_spacing, Character prior_character)
{
auto handle_scaled = reinterpret_cast<FontFaceHandleScaled*>(handle);
return handle_scaled->GetStringWidth(string, letter_spacing, prior_character);
}
int FontEngineInterfaceScaled::GenerateString(FontFaceHandle handle, FontEffectsHandle font_effects_handle, const String& string,
const Vector2f& position, const Colourb& colour, float opacity, float letter_spacing, GeometryList& geometry)
{
auto handle_scaled = reinterpret_cast<FontFaceHandleScaled*>(handle);
return handle_scaled->GenerateString(geometry, string, position, colour, opacity, letter_spacing, (int)font_effects_handle);
}
int FontEngineInterfaceScaled::GetVersion(FontFaceHandle handle)
{
auto handle_scaled = reinterpret_cast<FontFaceHandleScaled*>(handle);
return handle_scaled->GetVersion();
}
void FontEngineInterfaceScaled::ReleaseFontResources()
{
FontProvider::ReleaseFontResources();
}
} // namespace Rml

View File

@ -1,75 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
*
* 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.
*
*/
#ifndef RMLUI_CORE_FONTENGINESCALED_FONTENGINEINTERFACESCALED_H
#define RMLUI_CORE_FONTENGINESCALED_FONTENGINEINTERFACESCALED_H
#include "RmlUi/Core/FontEngineInterface.h"
namespace RecompRml {
using namespace Rml;
class RMLUICORE_API FontEngineInterfaceScaled : public FontEngineInterface {
public:
FontEngineInterfaceScaled();
virtual ~FontEngineInterfaceScaled();
/// Adds a new font face to the database. The face's family, style and weight will be determined from the face itself.
bool LoadFontFace(const String& file_name, bool fallback_face, Style::FontWeight weight) override;
/// Adds a new font face to the database using the provided family, style and weight.
bool LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style, Style::FontWeight weight,
bool fallback_face) override;
/// Returns a handle to a font face that can be used to position and render text. This will return the closest match
/// it can find, but in the event a font family is requested that does not exist, NULL will be returned instead of a
/// valid handle.
FontFaceHandle GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size) override;
/// Prepares for font effects by configuring a new, or returning an existing, layer configuration.
FontEffectsHandle PrepareFontEffects(FontFaceHandle, const FontEffectList& font_effects) override;
/// Returns the font metrics of the given font face.
const FontMetrics& GetFontMetrics(FontFaceHandle handle) override;
/// Returns the width a string will take up if rendered with this handle.
int GetStringWidth(FontFaceHandle, const String& string, float letter_spacing, Character prior_character) override;
/// Generates the geometry required to render a single line of text.
int GenerateString(FontFaceHandle, FontEffectsHandle, const String& string, const Vector2f& position, const Colourb& colour, float opacity,
float letter_spacing, GeometryList& geometry) override;
/// Returns the current version of the font face.
int GetVersion(FontFaceHandle handle) override;
/// Releases resources owned by sized font faces, including their textures and rendered glyphs.
void ReleaseFontResources() override;
};
} // namespace Rml
#endif

View File

@ -1,95 +0,0 @@
/*
* This source file is modified from a part of RmlUi the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#include "FontFace.h"
#include "RmlUi/Core/Log.h"
#include "FontFaceHandleScaled.h"
#include "FreeTypeInterface.h"
namespace RecompRml {
using namespace Rml;
FontFace::FontFace(FontFaceHandleFreetype _face, Style::FontStyle _style, Style::FontWeight _weight)
{
style = _style;
weight = _weight;
face = _face;
}
FontFace::~FontFace()
{
if (face)
FreeType::ReleaseFace(face);
}
Style::FontStyle FontFace::GetStyle() const
{
return style;
}
Style::FontWeight FontFace::GetWeight() const
{
return weight;
}
FontFaceHandleScaled* FontFace::GetHandle(int size, bool load_default_glyphs)
{
auto it = handles.find(size);
if (it != handles.end())
return it->second.get();
// See if this face has been released.
if (!face)
{
Log::Message(Log::LT_WARNING, "Font face has been released, unable to generate new handle.");
return nullptr;
}
// Construct and initialise the new handle.
auto handle = MakeUnique<FontFaceHandleScaled>();
if (!handle->Initialize(face, size, load_default_glyphs))
{
handles[size] = nullptr;
return nullptr;
}
FontFaceHandleScaled* result = handle.get();
// Save the new handle to the font face
handles[size] = std::move(handle);
return result;
}
void FontFace::ReleaseFontResources()
{
HandleMap().swap(handles);
}
} // namespace Rml

View File

@ -1,74 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#ifndef RMLUI_CORE_FONTENGINESCALED_FONTFACE_H
#define RMLUI_CORE_FONTENGINESCALED_FONTFACE_H
#include "RmlUi/Core/StyleTypes.h"
#include "FontTypes.h"
namespace RecompRml {
using namespace Rml;
class FontFaceHandleScaled;
/**
@author Peter Curry
*/
class FontFace {
public:
FontFace(FontFaceHandleFreetype face, Style::FontStyle style, Style::FontWeight weight);
~FontFace();
Style::FontStyle GetStyle() const;
Style::FontWeight GetWeight() const;
/// Returns a handle for positioning and rendering this face at the given size.
/// @param[in] size The size of the desired handle, in points.
/// @param[in] load_default_glyphs True to load the default set of glyph (ASCII range).
/// @return The font handle.
FontFaceHandleScaled* GetHandle(int size, bool load_default_glyphs);
/// Releases resources owned by sized font faces, including their textures and rendered glyphs.
void ReleaseFontResources();
private:
Style::FontStyle style;
Style::FontWeight weight;
// Key is font size
using HandleMap = UnorderedMap<int, UniquePtr<FontFaceHandleScaled>>;
HandleMap handles;
FontFaceHandleFreetype face;
};
} // namespace Rml
#endif

View File

@ -1,487 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#include "FontFaceHandleScaled.h"
#include "RmlUi/Core.h"
#include "FontFaceLayer.h"
#include "FontProvider.h"
#include "FreeTypeInterface.h"
#include <algorithm>
namespace RecompRml {
using namespace Rml;
static constexpr char32_t KerningCache_AsciiSubsetBegin = 32;
static constexpr char32_t KerningCache_AsciiSubsetLast = 126;
FontFaceHandleScaled::FontFaceHandleScaled()
{
base_layer = nullptr;
metrics = {};
ft_face = 0;
}
FontFaceHandleScaled::~FontFaceHandleScaled()
{
glyphs.clear();
layers.clear();
}
bool FontFaceHandleScaled::Initialize(FontFaceHandleFreetype face, int font_size, bool load_default_glyphs)
{
ft_face = face;
RMLUI_ASSERTMSG(layer_configurations.empty(), "Initialize must only be called once.");
if (!FreeType::InitialiseFaceHandle(ft_face, font_size, glyphs, metrics, load_default_glyphs))
return false;
has_kerning = FreeType::HasKerning(ft_face);
FillKerningPairCache();
// Generate the default layer and layer configuration.
base_layer = GetOrCreateLayer(nullptr);
layer_configurations.push_back(LayerConfiguration{base_layer});
return true;
}
const FontMetrics& FontFaceHandleScaled::GetFontMetrics() const
{
return metrics;
}
const FontGlyphMap& FontFaceHandleScaled::GetGlyphs() const
{
return glyphs;
}
int FontFaceHandleScaled::GetStringWidth(const String& string, float letter_spacing, Character prior_character)
{
RMLUI_ZoneScoped;
int width = 0;
for (auto it_string = StringIteratorU8(string); it_string; ++it_string)
{
Character character = *it_string;
const FontGlyph* glyph = GetOrAppendGlyph(character);
if (!glyph)
continue;
// Adjust the cursor for the kerning between this character and the previous one.
width += GetKerning(prior_character, character);
// Adjust the cursor for this character's advance.
width += glyph->advance;
width += (int)letter_spacing;
prior_character = character;
}
return Math::Max(width, 0);
}
int FontFaceHandleScaled::GenerateLayerConfiguration(const FontEffectList& font_effects)
{
if (font_effects.empty())
return 0;
// Check each existing configuration for a match with this arrangement of effects.
int configuration_index = 1;
for (; configuration_index < (int)layer_configurations.size(); ++configuration_index)
{
const LayerConfiguration& configuration = layer_configurations[configuration_index];
// Check the size is correct. For a match, there should be one layer in the configuration
// plus an extra for the base layer.
if (configuration.size() != font_effects.size() + 1)
continue;
// Check through each layer, checking it was created by the same effect as the one we're
// checking.
size_t effect_index = 0;
for (size_t i = 0; i < configuration.size(); ++i)
{
// Skip the base layer ...
if (configuration[i]->GetFontEffect() == nullptr)
continue;
// If the ith layer's effect doesn't match the equivalent effect, then this
// configuration can't match.
if (configuration[i]->GetFontEffect() != font_effects[effect_index].get())
break;
// Check the next one ...
++effect_index;
}
if (effect_index == font_effects.size())
return configuration_index;
}
// No match, so we have to generate a new layer configuration.
layer_configurations.push_back(LayerConfiguration());
LayerConfiguration& layer_configuration = layer_configurations.back();
bool added_base_layer = false;
for (size_t i = 0; i < font_effects.size(); ++i)
{
if (!added_base_layer && font_effects[i]->GetLayer() == FontEffect::Layer::Front)
{
layer_configuration.push_back(base_layer);
added_base_layer = true;
}
FontFaceLayer* new_layer = GetOrCreateLayer(font_effects[i]);
layer_configuration.push_back(new_layer);
}
// Add the base layer now if we still haven't added it.
if (!added_base_layer)
layer_configuration.push_back(base_layer);
return (int)(layer_configurations.size() - 1);
}
bool FontFaceHandleScaled::GenerateLayerTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, const FontEffect* font_effect,
int texture_id, int handle_version) const
{
if (handle_version != version)
{
RMLUI_ERRORMSG("While generating font layer texture: Handle version mismatch in texture vs font-face.");
return false;
}
auto it = std::find_if(layers.begin(), layers.end(), [font_effect](const EffectLayerPair& pair) { return pair.font_effect == font_effect; });
if (it == layers.end())
{
RMLUI_ERRORMSG("While generating font layer texture: Layer id not found.");
return false;
}
return it->layer->GenerateTexture(texture_data, texture_dimensions, texture_id, glyphs);
}
int FontFaceHandleScaled::GenerateString(GeometryList& geometry, const String& string, const Vector2f position, const Colourb colour,
const float opacity, const float letter_spacing, const int layer_configuration_index)
{
int geometry_index = 0;
int line_width = 0;
RMLUI_ASSERT(layer_configuration_index >= 0);
RMLUI_ASSERT(layer_configuration_index < (int)layer_configurations.size());
UpdateLayersOnDirty();
// Fetch the requested configuration and generate the geometry for each one.
const LayerConfiguration& layer_configuration = layer_configurations[layer_configuration_index];
// Reserve for the common case of one texture per layer.
geometry.reserve(layer_configuration.size());
for (size_t i = 0; i < layer_configuration.size(); ++i)
{
FontFaceLayer* layer = layer_configuration[i];
Colourb layer_colour;
if (layer == base_layer)
{
layer_colour = colour;
}
else
{
layer_colour = layer->GetColour();
if (opacity < 1.f)
layer_colour.alpha = byte(opacity * float(layer_colour.alpha));
}
const int num_textures = layer->GetNumTextures();
if (num_textures == 0)
continue;
// Resize the geometry list if required.
if ((int)geometry.size() < geometry_index + num_textures)
geometry.resize(geometry_index + num_textures);
RMLUI_ASSERT(geometry_index < (int)geometry.size());
// Bind the textures to the geometries.
for (int tex_index = 0; tex_index < num_textures; ++tex_index)
geometry[geometry_index + tex_index].SetTexture(layer->GetTexture(tex_index));
line_width = 0;
Character prior_character = Character::Null;
geometry[geometry_index].GetIndices().reserve(string.size() * 6);
geometry[geometry_index].GetVertices().reserve(string.size() * 4);
for (auto it_string = StringIteratorU8(string); it_string; ++it_string)
{
Character character = *it_string;
const FontGlyph* glyph = GetOrAppendGlyph(character);
if (!glyph)
continue;
// Adjust the cursor for the kerning between this character and the previous one.
line_width += GetKerning(prior_character, character);
// Use white vertex colors on RGB glyphs.
const Colourb glyph_color =
(layer == base_layer && glyph->color_format == ColorFormat::RGBA8 ? Colourb(255, layer_colour.alpha) : layer_colour);
layer->GenerateGeometry(&geometry[geometry_index], character, Vector2f(position.x + line_width, position.y), glyph_color);
line_width += glyph->advance;
line_width += (int)letter_spacing;
prior_character = character;
}
geometry_index += num_textures;
}
// Cull any excess geometry from a previous generation.
geometry.resize(geometry_index);
return Math::Max(line_width, 0);
}
bool FontFaceHandleScaled::UpdateLayersOnDirty()
{
bool result = false;
// If we are dirty, regenerate all the layers and increment the version
if (is_layers_dirty && base_layer)
{
is_layers_dirty = false;
++version;
// Regenerate all the layers.
// Note: The layer regeneration needs to happen in the order in which the layers were created,
// otherwise we may end up cloning a layer which has not yet been regenerated. This means trouble!
for (auto& pair : layers)
{
GenerateLayer(pair.layer.get());
}
result = true;
}
return result;
}
int FontFaceHandleScaled::GetVersion() const
{
return version;
}
bool FontFaceHandleScaled::AppendGlyph(Character character)
{
bool result = FreeType::AppendGlyph(ft_face, metrics.size, character, glyphs);
return result;
}
void FontFaceHandleScaled::FillKerningPairCache()
{
if (!has_kerning)
return;
for (char32_t i = KerningCache_AsciiSubsetBegin; i <= KerningCache_AsciiSubsetLast; i++)
{
for (char32_t j = KerningCache_AsciiSubsetBegin; j <= KerningCache_AsciiSubsetLast; j++)
{
const bool first_iteration = (i == KerningCache_AsciiSubsetBegin && j == KerningCache_AsciiSubsetBegin);
// Fetch the kerning from the font face. Submit zero font size on subsequent iterations for performance reasons.
const int kerning = FreeType::GetKerning(ft_face, first_iteration ? metrics.size : 0, Character(i), Character(j));
if (kerning != 0)
{
kerning_pair_cache.emplace(AsciiPair((i << 8) | j), KerningIntType(kerning));
}
}
}
}
int FontFaceHandleScaled::GetKerning(Character lhs, Character rhs) const
{
static_assert(' ' == 32, "Only ASCII/UTF8 character set supported.");
// Check if we have no kerning, or if we are have a control character.
if (!has_kerning || char32_t(lhs) < ' ' || char32_t(rhs) < ' ')
return 0;
// See if the kerning pair has been cached.
const bool lhs_in_cache = (char32_t(lhs) >= KerningCache_AsciiSubsetBegin && char32_t(lhs) <= KerningCache_AsciiSubsetLast);
const bool rhs_in_cache = (char32_t(rhs) >= KerningCache_AsciiSubsetBegin && char32_t(rhs) <= KerningCache_AsciiSubsetLast);
if (lhs_in_cache && rhs_in_cache)
{
const auto it = kerning_pair_cache.find(AsciiPair((int(lhs) << 8) | int(rhs)));
if (it != kerning_pair_cache.end())
{
return it->second;
}
return 0;
}
// Fetch it from the font face instead.
const int result = FreeType::GetKerning(ft_face, metrics.size, lhs, rhs);
return result;
}
const FontGlyph* FontFaceHandleScaled::GetOrAppendGlyph(Character& character, bool look_in_fallback_fonts)
{
// Don't try to render control characters
if ((char32_t)character < (char32_t)' ')
return nullptr;
auto it_glyph = glyphs.find(character);
if (it_glyph == glyphs.end())
{
bool result = AppendGlyph(character);
if (result)
{
it_glyph = glyphs.find(character);
if (it_glyph == glyphs.end())
{
RMLUI_ERROR;
return nullptr;
}
is_layers_dirty = true;
}
else if (look_in_fallback_fonts)
{
const int num_fallback_faces = FontProvider::CountFallbackFontFaces();
for (int i = 0; i < num_fallback_faces; i++)
{
FontFaceHandleScaled* fallback_face = FontProvider::GetFallbackFontFace(i, metrics.size);
if (!fallback_face || fallback_face == this)
continue;
const FontGlyph* glyph = fallback_face->GetOrAppendGlyph(character, false);
if (glyph)
{
// Insert the new glyph into our own set of glyphs
auto pair = glyphs.emplace(character, glyph->WeakCopy());
it_glyph = pair.first;
if (pair.second)
is_layers_dirty = true;
break;
}
}
// If we still have not found a glyph, use the replacement character.
if (it_glyph == glyphs.end())
{
character = Character::Replacement;
it_glyph = glyphs.find(character);
if (it_glyph == glyphs.end())
return nullptr;
}
}
else
{
return nullptr;
}
}
const FontGlyph* glyph = &it_glyph->second;
return glyph;
}
FontFaceLayer* FontFaceHandleScaled::GetOrCreateLayer(const SharedPtr<const FontEffect>& font_effect)
{
// Search for the font effect layer first, it may have been instanced before as part of a different configuration.
const FontEffect* font_effect_ptr = font_effect.get();
auto it =
std::find_if(layers.begin(), layers.end(), [font_effect_ptr](const EffectLayerPair& pair) { return pair.font_effect == font_effect_ptr; });
if (it != layers.end())
return it->layer.get();
// No existing effect matches, generate a new layer for the effect.
layers.push_back(EffectLayerPair{font_effect_ptr, nullptr});
auto& layer = layers.back().layer;
layer = MakeUnique<FontFaceLayer>(font_effect);
GenerateLayer(layer.get());
return layer.get();
}
bool FontFaceHandleScaled::GenerateLayer(FontFaceLayer* layer)
{
RMLUI_ASSERT(layer);
const FontEffect* font_effect = layer->GetFontEffect();
bool result = false;
if (!font_effect)
{
result = layer->Generate(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.
result = layer->Generate(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;
}
return result;
}
} // namespace Rml

View File

@ -1,157 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#ifndef RMLUI_CORE_FONTENGINESCALED_FONTFACEHANDLE_H
#define RMLUI_CORE_FONTENGINESCALED_FONTFACEHANDLE_H
#include "RmlUi/Core.h"
///FontEffect.h"
// #include "../../../Include/RmlUi/Core/FontGlyph.h"
// #include "../../../Include/RmlUi/Core/FontMetrics.h"
// #include "../../../Include/RmlUi/Core/Geometry.h"
// #include "../../../Include/RmlUi/Core/Texture.h"
// #include "../../../Include/RmlUi/Core/Traits.h"
#include "FontTypes.h"
namespace RecompRml {
using namespace Rml;
class FontFaceLayer;
/**
@author Peter Curry
*/
class FontFaceHandleScaled final : public NonCopyMoveable {
public:
FontFaceHandleScaled();
~FontFaceHandleScaled();
bool Initialize(FontFaceHandleFreetype face, int font_size, bool load_default_glyphs);
const FontMetrics& GetFontMetrics() const;
const FontGlyphMap& GetGlyphs() const;
/// Returns the width a string will take up if rendered with this 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.
/// @return The width, in pixels, this string will occupy if rendered with this handle.
int GetStringWidth(const String& string, float letter_spacing, Character prior_character = Character::Null);
/// Generates, if required, the layer configuration for a given list of font effects.
/// @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.
int GenerateLayerConfiguration(const FontEffectList& font_effects);
/// 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] font_effect The font effect used for the layer.
/// @param[in] texture_id The index of the texture within the layer to generate.
/// @param[in] handle_version The version of the handle data. Function returns false if out of date.
bool GenerateLayerTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, const FontEffect* font_effect, int texture_id,
int handle_version) const;
/// Generates the geometry required to render a single line of text.
/// @param[out] geometry An array of geometries to generate the geometry into.
/// @param[in] string The string to render.
/// @param[in] position The position of the baseline of the first character to render.
/// @param[in] colour The colour to render the text.
/// @param[in] opacity The opacity of the text, should be applied to font effects.
/// @param[in] layer_configuration Face configuration index to use for generating string.
/// @return The width, in pixels, of the string geometry.
int GenerateString(GeometryList& geometry, const String& string, Vector2f position, Colourb colour, float opacity, float letter_spacing,
int layer_configuration = 0);
/// Version is changed whenever the layers are dirtied, requiring regeneration of string geometry.
int GetVersion() const;
private:
// Build and append glyph to 'glyphs'
bool AppendGlyph(Character character);
// Build a kerning cache for common characters.
void FillKerningPairCache();
// Return the kerning for a character pair.
int GetKerning(Character lhs, Character rhs) const;
/// Retrieve a glyph from the given code point, building and appending a new glyph if not already built.
/// @param[in-out] character The character, can be changed e.g. to the replacement character if no glyph is found.
/// @param[in] look_in_fallback_fonts Look for the glyph in fallback fonts if not found locally, adding it to our glyphs.
/// @return The font glyph for the returned code point.
const FontGlyph* GetOrAppendGlyph(Character& character, bool look_in_fallback_fonts = true);
// Regenerate layers if dirty, such as after adding new glyphs.
bool UpdateLayersOnDirty();
// Create a new layer from the given font effect if it does not already exist.
FontFaceLayer* GetOrCreateLayer(const SharedPtr<const FontEffect>& font_effect);
// (Re-)generate a layer in this font face handle.
bool GenerateLayer(FontFaceLayer* layer);
FontGlyphMap glyphs;
struct EffectLayerPair {
const FontEffect* font_effect;
UniquePtr<FontFaceLayer> layer;
};
using FontLayerMap = Vector<EffectLayerPair>;
using FontLayerCache = SmallUnorderedMap<size_t, FontFaceLayer*>;
using LayerConfiguration = Vector<FontFaceLayer*>;
using LayerConfigurationList = Vector<LayerConfiguration>;
// The list of all font layers, index by the effect that instanced them.
FontFaceLayer* base_layer;
FontLayerMap layers;
// Each font layer that generated geometry or textures, indexed by the font-effect's fingerprint key.
FontLayerCache layer_cache;
// Pre-cache kerning pairs for some ascii subset of all characters.
using AsciiPair = uint16_t;
using KerningIntType = int16_t;
using KerningPairs = UnorderedMap<AsciiPair, KerningIntType>;
KerningPairs kerning_pair_cache;
bool has_kerning = false;
bool is_layers_dirty = false;
int version = 0;
// All configurations currently in use on this handle. New configurations will be generated as required.
LayerConfigurationList layer_configurations;
FontMetrics metrics;
FontFaceHandleFreetype ft_face;
};
} // namespace Rml
#endif

View File

@ -1,275 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#include "FontFaceLayer.h"
#include "RmlUi/Core/Log.h"
#include "RmlUi/Core/RenderInterface.h"
#include "FontFaceHandleScaled.h"
#include <string.h>
namespace RecompRml {
using namespace Rml;
FontFaceLayer::FontFaceLayer(const SharedPtr<const FontEffect>& _effect) : colour(255, 255, 255)
{
effect = _effect;
if (effect)
colour = effect->GetColour();
}
FontFaceLayer::~FontFaceLayer() {}
bool FontFaceLayer::Generate(const FontFaceHandleScaled* handle, const FontFaceLayer* clone, bool clone_glyph_origins)
{
// Clear the old layout if it exists.
{
// @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.
texture_layout = TextureLayout{};
character_boxes.clear();
textures.clear();
}
const FontGlyphMap& glyphs = handle->GetGlyphs();
// Generate the new layout.
if (clone)
{
// Clone the geometry and textures from the clone layer.
character_boxes = clone->character_boxes;
// 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) and adjust the origins as appropriate.
if (effect && !clone_glyph_origins)
{
for (auto& pair : glyphs)
{
Character character = pair.first;
const FontGlyph& glyph = pair.second;
auto it = character_boxes.find(character);
if (it == character_boxes.end())
{
// This can happen if the layers have been dirtied in FontHandleScaled. We will
// probably be regenerated soon, just skip the character for now.
continue;
}
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;
}
}
}
else
{
// Initialise the texture layout for the glyphs.
character_boxes.reserve(glyphs.size());
for (auto& pair : glyphs)
{
Character character = 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;
}
Vector2i scaled_origin = glyph_origin * global_font_scale;
Vector2i scaled_dimensions = glyph_dimensions * global_font_scale;
TextureBox box;
box.origin = Vector2f(float(scaled_origin.x + glyph.bearing.x), float(scaled_origin.y - glyph.bearing.y));
box.dimensions = Vector2f(scaled_dimensions);
RMLUI_ASSERT(box.dimensions.x >= 0 && box.dimensions.y >= 0);
character_boxes[character] = box;
// Add the character's dimensions into the texture layout engine.
texture_layout.AddRectangle((int)character, glyph_dimensions);
}
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(max_texture_dimensions))
return false;
// Iterate over each rectangle in the layout, copying the glyph data into the rectangle as
// appropriate and generating geometry.
for (int i = 0; i < texture_layout.GetNumRectangles(); ++i)
{
TextureLayoutRectangle& rectangle = texture_layout.GetRectangle(i);
const TextureLayoutTexture& texture = texture_layout.GetTexture(rectangle.GetTextureIndex());
Character character = (Character)rectangle.GetId();
RMLUI_ASSERT(character_boxes.find(character) != character_boxes.end());
TextureBox& box = character_boxes[character];
// Set the character's texture index.
box.texture_index = rectangle.GetTextureIndex();
// Generate the character's texture coordinates.
box.texcoords[0].x = float(rectangle.GetPosition().x) / float(texture.GetDimensions().x);
box.texcoords[0].y = float(rectangle.GetPosition().y) / float(texture.GetDimensions().y);
box.texcoords[1].x = float(rectangle.GetPosition().x + rectangle.GetDimensions().x) / float(texture.GetDimensions().x);
box.texcoords[1].y = float(rectangle.GetPosition().y + rectangle.GetDimensions().y) / float(texture.GetDimensions().y);
}
const FontEffect* effect_ptr = effect.get();
const int handle_version = handle->GetVersion();
// Generate the textures.
for (int i = 0; i < texture_layout.GetNumTextures(); ++i)
{
const int texture_id = i;
TextureCallback texture_callback = [handle, effect_ptr, texture_id, handle_version](RenderInterface* render_interface,
const String& /*name*/, TextureHandle& out_texture_handle, Vector2i& out_dimensions) -> bool {
UniquePtr<const byte[]> data;
if (!handle->GenerateLayerTexture(data, out_dimensions, effect_ptr, texture_id, handle_version) || !data)
return false;
if (!render_interface->GenerateTexture(out_texture_handle, data.get(), out_dimensions))
return false;
return true;
};
Texture texture;
texture.Set("font-face-layer", texture_callback);
textures.push_back(texture);
}
}
return true;
}
bool FontFaceLayer::GenerateTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id, const FontGlyphMap& glyphs)
{
if (texture_id < 0 || texture_id > texture_layout.GetNumTextures())
return false;
// Generate the texture data.
texture_data = texture_layout.GetTexture(texture_id).AllocateTexture();
texture_dimensions = texture_layout.GetTexture(texture_id).GetDimensions();
for (int i = 0; i < texture_layout.GetNumRectangles(); ++i)
{
TextureLayoutRectangle& rectangle = texture_layout.GetRectangle(i);
Character character = (Character)rectangle.GetId();
RMLUI_ASSERT(character_boxes.find(character) != character_boxes.end());
TextureBox& box = character_boxes[character];
if (box.texture_index != texture_id)
continue;
auto it = glyphs.find((Character)rectangle.GetId());
if (it == glyphs.end())
continue;
const FontGlyph& glyph = it->second;
if (effect == nullptr)
{
// Copy the glyph's bitmap data into its allocated texture.
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);
for (int j = 0; j < glyph.bitmap_dimensions.y; ++j)
{
switch (glyph.color_format)
{
case ColorFormat::A8:
{
for (int k = 0; k < num_bytes_per_line; ++k)
destination[k * 4 + 3] = source[k];
}
break;
case ColorFormat::RGBA8:
{
memcpy(destination, source, num_bytes_per_line);
}
break;
}
destination += rectangle.GetTextureStride();
source += num_bytes_per_line;
}
}
}
else
{
effect->GenerateGlyphTexture(rectangle.GetTextureData(), Vector2i(box.dimensions), rectangle.GetTextureStride(), glyph);
}
}
return true;
}
const FontEffect* FontFaceLayer::GetFontEffect() const
{
return effect.get();
}
const Texture* FontFaceLayer::GetTexture(int index)
{
RMLUI_ASSERT(index >= 0);
RMLUI_ASSERT(index < GetNumTextures());
return &(textures[index]);
}
int FontFaceLayer::GetNumTextures() const
{
return (int)textures.size();
}
Colourb FontFaceLayer::GetColour() const
{
return colour;
}
} // namespace Rml

View File

@ -1,138 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#ifndef RMLUI_CORE_FONTENGINESCALED_FONTFACELAYER_H
#define RMLUI_CORE_FONTENGINESCALED_FONTFACELAYER_H
#include "RmlUi/Core.h"
#include "RmlUi/Core/FontGlyph.h"
#include "RmlUi/../../Source/Core/TextureLayout.h"
namespace Rml {
class FontEffect;
}
namespace RecompRml {
using namespace Rml;
class FontFaceHandleScaled;
/**
A textured layer stored as part of a font face handle. Each handle will have at least a base
layer for the standard font. Further layers can be added to allow rendering of text effects.
@author Peter Curry
*/
class FontFaceLayer {
public:
FontFaceLayer(const SharedPtr<const FontEffect>& _effect);
~FontFaceLayer();
/// Generates or re-generates the character and texture data for the layer.
/// @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.
/// @return True if the layer was generated successfully, false if not.
bool Generate(const FontFaceHandleScaled* 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] texture_id The index of the texture within the layer to generate.
/// @param[in] glyphs The glyphs required by the font face handle.
bool GenerateTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id, const FontGlyphMap& glyphs);
/// 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.
/// @param[in] position The position of the baseline.
/// @param[in] colour The colour of the string.
inline void GenerateGeometry(Geometry* geometry, const Character character_code, const Vector2f position, const Colourb colour) const
{
auto it = character_boxes.find(character_code);
if (it == character_boxes.end())
return;
const TextureBox& box = it->second;
if (box.texture_index < 0)
return;
// Generate the geometry for the character.
Vector<Vertex>& character_vertices = geometry[box.texture_index].GetVertices();
Vector<int>& character_indices = geometry[box.texture_index].GetIndices();
character_vertices.resize(character_vertices.size() + 4);
character_indices.resize(character_indices.size() + 6);
GeometryUtilities::GenerateQuad(&character_vertices[0] + (character_vertices.size() - 4),
&character_indices[0] + (character_indices.size() - 6), Vector2f(position.x + box.origin.x, position.y + box.origin.y).Round(),
box.dimensions, colour, box.texcoords[0], box.texcoords[1], (int)character_vertices.size() - 4);
}
/// Returns the effect used to generate the layer.
const FontEffect* GetFontEffect() const;
/// Returns one of the layer's textures.
const Texture* GetTexture(int index);
/// Returns the number of textures employed by this layer.
int GetNumTextures() const;
/// Returns the layer's colour.
Colourb GetColour() const;
private:
struct TextureBox {
TextureBox() : texture_index(-1) {}
// The offset, in pixels, of the baseline from the start of this character's geometry.
Vector2f origin;
// The width and height, in pixels, of this character's geometry.
Vector2f dimensions;
// The texture coordinates for the character's geometry.
Vector2f texcoords[2];
// The texture this character renders from.
int texture_index;
};
using CharacterMap = UnorderedMap<Character, TextureBox>;
using TextureList = Vector<Texture>;
SharedPtr<const FontEffect> effect;
TextureLayout texture_layout;
CharacterMap character_boxes;
TextureList textures;
Colourb colour;
};
} // namespace Rml
#endif

View File

@ -1,97 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#include "FontFamily.h"
#include "RmlUi/Core/ComputedValues.h"
#include "RmlUi/Core/Math.h"
#include "FontFace.h"
#include <limits.h>
namespace RecompRml {
using namespace Rml;
FontFamily::FontFamily(const String& name) : name(name) {}
FontFamily::~FontFamily()
{
// Multiple face entries may share memory within a single font family, although only one of them owns it. Here we make sure that all the face
// destructors are run before all the memory is released. This way we don't leave any hanging references to invalidated memory.
for (FontFaceEntry& entry : font_faces)
entry.face.reset();
}
FontFaceHandleScaled* FontFamily::GetFaceHandle(Style::FontStyle style, Style::FontWeight weight, int size)
{
int best_dist = INT_MAX;
FontFace* matching_face = nullptr;
for (size_t i = 0; i < font_faces.size(); i++)
{
FontFace* face = font_faces[i].face.get();
if (face->GetStyle() == style)
{
const int dist = Math::Absolute((int)face->GetWeight() - (int)weight);
if (dist == 0)
{
// Direct match for weight, break the loop early.
matching_face = face;
break;
}
else if (dist < best_dist)
{
// Best match so far for weight, store the face and dist.
matching_face = face;
best_dist = dist;
}
}
}
if (!matching_face)
return nullptr;
return matching_face->GetHandle(size, true);
}
FontFace* FontFamily::AddFace(FontFaceHandleFreetype ft_face, Style::FontStyle style, Style::FontWeight weight, UniquePtr<byte[]> face_memory)
{
auto face = MakeUnique<FontFace>(ft_face, style, weight);
FontFace* result = face.get();
font_faces.push_back(FontFaceEntry{std::move(face), std::move(face_memory)});
return result;
}
void FontFamily::ReleaseFontResources()
{
for (auto& entry : font_faces)
entry.face->ReleaseFontResources();
}
} // namespace Rml

View File

@ -1,82 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#ifndef RMLUI_CORE_FONTENGINESCALED_FONTFAMILY_H
#define RMLUI_CORE_FONTENGINESCALED_FONTFAMILY_H
#include "FontTypes.h"
namespace RecompRml {
using namespace Rml;
class FontFace;
class FontFaceHandleScaled;
/**
@author Peter Curry
*/
class FontFamily {
public:
FontFamily(const String& name);
~FontFamily();
/// Returns a handle to the most appropriate font in the family, at the correct size.
/// @param[in] style The style of the desired handle.
/// @param[in] weight The weight of the desired handle.
/// @param[in] size The size of desired handle, in points.
/// @return A valid handle if a matching (or closely matching) font face was found, nullptr otherwise.
FontFaceHandleScaled* GetFaceHandle(Style::FontStyle style, Style::FontWeight weight, int size);
/// Adds a new face to the family.
/// @param[in] ft_face The previously loaded FreeType face.
/// @param[in] style The style of the new face.
/// @param[in] weight The weight of the new face.
/// @param[in] face_memory Optionally pass ownership of the face's memory to the face itself, automatically releasing it on destruction.
/// @return True if the face was loaded successfully, false otherwise.
FontFace* AddFace(FontFaceHandleFreetype ft_face, Style::FontStyle style, Style::FontWeight weight, UniquePtr<byte[]> face_memory);
/// Releases resources owned by sized font faces, including their textures and rendered glyphs.
void ReleaseFontResources();
protected:
String name;
struct FontFaceEntry {
UniquePtr<FontFace> face;
// Only filled if we own the memory used by the face's FreeType handle. May be shared with other faces in this family.
UniquePtr<byte[]> face_memory;
};
using FontFaceList = Vector<FontFaceEntry>;
FontFaceList font_faces;
};
} // namespace Rml
#endif

View File

@ -1,268 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#include "FontProvider.h"
#include "RmlUi/Core/Core.h"
#include "RmlUi/Core/FileInterface.h"
#include "RmlUi/Core/Log.h"
#include "RmlUi/Core/Math.h"
#include "RmlUi/Core/StringUtilities.h"
#include "RmlUi/../../Source/Core/ComputeProperty.h"
#include "FontFace.h"
#include "FontFamily.h"
#include "FreeTypeInterface.h"
#include <algorithm>
namespace RecompRml {
using namespace Rml;
static FontProvider* g_font_provider = nullptr;
FontProvider::FontProvider()
{
RMLUI_ASSERT(!g_font_provider);
}
FontProvider::~FontProvider()
{
RMLUI_ASSERT(g_font_provider == this);
}
bool FontProvider::Initialise()
{
RMLUI_ASSERT(!g_font_provider);
if (!FreeType::Initialise())
return false;
g_font_provider = new FontProvider;
return true;
}
void FontProvider::Shutdown()
{
RMLUI_ASSERT(g_font_provider);
delete g_font_provider;
g_font_provider = nullptr;
FreeType::Shutdown();
}
FontProvider& FontProvider::Get()
{
RMLUI_ASSERT(g_font_provider);
return *g_font_provider;
}
FontFaceHandleScaled* FontProvider::GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size)
{
RMLUI_ASSERTMSG(family == StringUtilities::ToLower(family), "Font family name must be converted to lowercase before entering here.");
FontFamilyMap& families = Get().font_families;
auto it = families.find(family);
if (it == families.end())
return nullptr;
return it->second->GetFaceHandle(style, weight, size);
}
int FontProvider::CountFallbackFontFaces()
{
return (int)Get().fallback_font_faces.size();
}
FontFaceHandleScaled* FontProvider::GetFallbackFontFace(int index, int font_size)
{
auto& faces = FontProvider::Get().fallback_font_faces;
if (index >= 0 && index < (int)faces.size())
return faces[index]->GetHandle(font_size, false);
return nullptr;
}
void FontProvider::ReleaseFontResources()
{
RMLUI_ASSERT(g_font_provider);
for (auto& name_family : g_font_provider->font_families)
name_family.second->ReleaseFontResources();
}
bool FontProvider::LoadFontFace(const String& file_name, bool fallback_face, Style::FontWeight weight)
{
FileInterface* file_interface = GetFileInterface();
FileHandle handle = file_interface->Open(file_name);
if (!handle)
{
Log::Message(Log::LT_ERROR, "Failed to load font face from %s, could not open file.", file_name.c_str());
return false;
}
size_t length = file_interface->Length(handle);
auto buffer_ptr = UniquePtr<byte[]>(new byte[length]);
byte* buffer = buffer_ptr.get();
file_interface->Read(buffer, length, handle);
file_interface->Close(handle);
bool result = Get().LoadFontFace(buffer, (int)length, fallback_face, std::move(buffer_ptr), file_name, {}, Style::FontStyle::Normal, weight);
return result;
}
bool FontProvider::LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style, Style::FontWeight weight,
bool fallback_face)
{
const String source = "memory";
bool result = Get().LoadFontFace(data, data_size, fallback_face, nullptr, source, font_family, style, weight);
return result;
}
bool FontProvider::LoadFontFace(const byte* data, int data_size, bool fallback_face, UniquePtr<byte[]> face_memory, const String& source,
String font_family, Style::FontStyle style, Style::FontWeight weight)
{
using Style::FontWeight;
Vector<FaceVariation> face_variations;
if (!FreeType::GetFaceVariations(data, data_size, face_variations))
{
Log::Message(Log::LT_ERROR, "Failed to load font face from '%s': Invalid or unsupported font face file format.", source.c_str());
return false;
}
Vector<FaceVariation> load_variations;
if (face_variations.empty())
{
load_variations.push_back(FaceVariation{Style::FontWeight::Auto, 0, 0});
}
else
{
// Iterate through all the face variations and pick the ones to load. The list is already sorted by (weight, width). When weight is set to
// 'auto' we load all the weights of the face. However, we only want to load one width for each weight.
for (auto it = face_variations.begin(); it != face_variations.end();)
{
if (weight != FontWeight::Auto && it->weight != weight)
{
++it;
continue;
}
// We don't currently have any way for users to select widths, so we search for a regular (medium) value here.
constexpr int search_width = 100;
const FontWeight current_weight = it->weight;
int best_width_distance = Math::Absolute((int)it->width - search_width);
auto it_best_width = it;
// Search forward to find the best 'width' with the same weight.
for (++it; it != face_variations.end(); ++it)
{
if (it->weight != current_weight)
break;
const int width_distance = Math::Absolute((int)it->width - search_width);
if (width_distance < best_width_distance)
{
best_width_distance = width_distance;
it_best_width = it;
}
}
load_variations.push_back(*it_best_width);
}
}
if (load_variations.empty())
{
Log::Message(Log::LT_ERROR, "Failed to load font face from '%s': Could not locate face with weight %d.", source.c_str(), (int)weight);
return false;
}
for (const FaceVariation& variation : load_variations)
{
FontFaceHandleFreetype ft_face = FreeType::LoadFace(data, data_size, source, variation.named_instance_index);
if (!ft_face)
return false;
if (font_family.empty())
FreeType::GetFaceStyle(ft_face, &font_family, &style, nullptr);
if (weight == FontWeight::Auto)
FreeType::GetFaceStyle(ft_face, nullptr, nullptr, &weight);
const FontWeight variation_weight = (variation.weight == FontWeight::Auto ? weight : variation.weight);
const String font_face_description = GetFontFaceDescription(font_family, style, variation_weight);
if (!AddFace(ft_face, font_family, style, variation_weight, fallback_face, std::move(face_memory)))
{
Log::Message(Log::LT_ERROR, "Failed to load font face %s from '%s'.", font_face_description.c_str(), source.c_str());
return false;
}
Log::Message(Log::LT_INFO, "Loaded font face %s from '%s'.", font_face_description.c_str(), source.c_str());
}
return true;
}
bool FontProvider::AddFace(FontFaceHandleFreetype face, const String& family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face,
UniquePtr<byte[]> face_memory)
{
if (family.empty() || weight == Style::FontWeight::Auto)
return false;
String family_lower = StringUtilities::ToLower(family);
FontFamily* font_family = nullptr;
auto it = font_families.find(family_lower);
if (it != font_families.end())
{
font_family = (FontFamily*)it->second.get();
}
else
{
auto font_family_ptr = MakeUnique<FontFamily>(family_lower);
font_family = font_family_ptr.get();
font_families[family_lower] = std::move(font_family_ptr);
}
FontFace* font_face_result = font_family->AddFace(face, style, weight, std::move(face_memory));
if (font_face_result && fallback_face)
{
auto it_fallback_face = std::find(fallback_font_faces.begin(), fallback_font_faces.end(), font_face_result);
if (it_fallback_face == fallback_font_faces.end())
{
fallback_font_faces.push_back(font_face_result);
}
}
return static_cast<bool>(font_face_result);
}
} // namespace Rml

View File

@ -1,102 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#ifndef RMLUI_CORE_FONTENGINESCALED_FONTPROVIDER_H
#define RMLUI_CORE_FONTENGINESCALED_FONTPROVIDER_H
#include "RmlUi/Core/StyleTypes.h"
#include "RmlUi/Core/Types.h"
#include "FontTypes.h"
namespace RecompRml {
using namespace Rml;
class FontFace;
class FontFamily;
class FontFaceHandleScaled;
/**
The font provider contains all font families currently in use by RmlUi.
@author Peter Curry
*/
class FontProvider {
public:
static bool Initialise();
static void Shutdown();
/// Returns a handle to a font face that can be used to position and render text. This will return the closest match
/// it can find, but in the event a font family is requested that does not exist, nullptr will be returned instead of a
/// valid handle.
/// @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.
/// @return A valid handle if a matching (or closely matching) font face was found, nullptr otherwise.
static FontFaceHandleScaled* GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size);
/// Adds a new font face to the database. The face's family, style and weight will be determined from the face itself.
static bool LoadFontFace(const String& file_name, bool fallback_face, Style::FontWeight weight = Style::FontWeight::Auto);
/// Adds a new font face from memory.
static bool LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style, Style::FontWeight weight,
bool fallback_face);
/// Return the number of fallback font faces.
static int CountFallbackFontFaces();
/// Return a font face handle with the given index, at the given font size.
static FontFaceHandleScaled* GetFallbackFontFace(int index, int font_size);
/// Releases resources owned by sized font faces, including their textures and rendered glyphs.
static void ReleaseFontResources();
private:
FontProvider();
~FontProvider();
static FontProvider& Get();
bool LoadFontFace(const byte* data, int data_size, bool fallback_face, UniquePtr<byte[]> face_memory, const String& source, String font_family,
Style::FontStyle style, Style::FontWeight weight);
bool AddFace(FontFaceHandleFreetype face, const String& family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face,
UniquePtr<byte[]> face_memory);
using FontFaceList = Vector<FontFace*>;
using FontFamilyMap = UnorderedMap<String, UniquePtr<FontFamily>>;
FontFamilyMap font_families;
FontFaceList fallback_font_faces;
static const String debugger_font_family_name;
};
} // namespace Rml
#endif

View File

@ -1,58 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#ifndef RMLUI_CORE_FONTENGINESCALED_FONTTYPES_H
#define RMLUI_CORE_FONTENGINESCALED_FONTTYPES_H
#include "RmlUi/Core/FontGlyph.h"
#include "RmlUi/Core/StyleTypes.h"
#include "RmlUi/Core/Types.h"
namespace RecompRml {
using namespace Rml;
constexpr int global_font_scale = 1;
using FontFaceHandleFreetype = uintptr_t;
struct FaceVariation {
Style::FontWeight weight;
uint16_t width;
int named_instance_index;
};
inline bool operator<(const FaceVariation& a, const FaceVariation& b)
{
if (a.weight == b.weight)
return a.width < b.width;
return a.weight < b.weight;
}
} // namespace Rml
#endif

View File

@ -1,602 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#include "FreeTypeInterface.h"
#include "RmlUi/Core/ComputedValues.h"
#include "RmlUi/Core/FontMetrics.h"
#include "RmlUi/Core/Log.h"
#include <algorithm>
#include <ft2build.h>
#include <limits.h>
#include <string.h>
#include FT_FREETYPE_H
#include FT_MULTIPLE_MASTERS_H
#include FT_TRUETYPE_TABLES_H
namespace RecompRml {
using namespace Rml;
static FT_Library ft_library = nullptr;
static bool BuildGlyph(FT_Face ft_face, Character character, FontGlyphMap& glyphs, float bitmap_scaling_factor);
static void BuildGlyphMap(FT_Face ft_face, int size, FontGlyphMap& glyphs, float bitmap_scaling_factor, bool load_default_glyphs);
static void GenerateMetrics(FT_Face ft_face, FontMetrics& metrics, float bitmap_scaling_factor);
static bool SetFontSize(FT_Face ft_face, int font_size, float& out_bitmap_scaling_factor);
static void BitmapDownscale(byte* bitmap_new, int new_width, int new_height, const byte* bitmap_source, int width, int height, int pitch,
ColorFormat color_format);
static int ConvertFixed16_16ToInt(int32_t fx)
{
return fx / 0x10000;
}
bool FreeType::Initialise()
{
RMLUI_ASSERT(!ft_library);
FT_Error result = FT_Init_FreeType(&ft_library);
if (result != 0)
{
Log::Message(Log::LT_ERROR, "Failed to initialise FreeType, error %d.", result);
Shutdown();
return false;
}
return true;
}
void FreeType::Shutdown()
{
if (ft_library != nullptr)
{
FT_Done_FreeType(ft_library);
ft_library = nullptr;
}
}
bool FreeType::GetFaceVariations(const byte* data, int data_length, Vector<FaceVariation>& out_face_variations)
{
RMLUI_ASSERT(ft_library);
FT_Face face = nullptr;
FT_Error error = FT_New_Memory_Face(ft_library, (const FT_Byte*)data, data_length, 0, &face);
if (error)
return false;
FT_MM_Var* var = nullptr;
error = FT_Get_MM_Var(face, &var);
if (error)
return true;
unsigned int axis_index_weight = 0;
unsigned int axis_index_width = 1;
const unsigned int num_axis = var->num_axis;
for (unsigned int i = 0; i < num_axis; i++)
{
switch (var->axis[i].tag)
{
case 0x77676874: // 'wght'
axis_index_weight = i;
break;
case 0x77647468: // 'wdth'
axis_index_width = i;
break;
}
}
if (num_axis > 0)
{
for (unsigned int i = 0; i < var->num_namedstyles; i++)
{
uint16_t weight = (axis_index_weight < num_axis ? (uint16_t)ConvertFixed16_16ToInt(var->namedstyle[i].coords[axis_index_weight]) : 0);
uint16_t width = (axis_index_width < num_axis ? (uint16_t)ConvertFixed16_16ToInt(var->namedstyle[i].coords[axis_index_width]) : 0);
int named_instance_index = (i + 1);
out_face_variations.push_back(FaceVariation{weight == 0 ? Style::FontWeight::Normal : (Style::FontWeight)weight,
width == 0 ? (uint16_t)100 : width, named_instance_index});
}
}
std::sort(out_face_variations.begin(), out_face_variations.end());
#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 9
FT_Done_MM_Var(ft_library, var);
#endif
FT_Done_Face(face);
return true;
}
FontFaceHandleFreetype FreeType::LoadFace(const byte* data, int data_length, const String& source, int named_style_index)
{
RMLUI_ASSERT(ft_library);
FT_Face face = nullptr;
FT_Error error = FT_New_Memory_Face(ft_library, (const FT_Byte*)data, data_length, (named_style_index << 16), &face);
if (error)
{
Log::Message(Log::LT_ERROR, "FreeType error %d while loading face from %s.", error, source.c_str());
return 0;
}
// Initialise the character mapping on the face.
if (face->charmap == nullptr)
{
FT_Select_Charmap(face, FT_ENCODING_APPLE_ROMAN);
if (face->charmap == nullptr)
{
Log::Message(Log::LT_ERROR, "Font face (from %s) does not contain a Unicode or Apple Roman character map.", source.c_str());
FT_Done_Face(face);
return 0;
}
}
return (FontFaceHandleFreetype)face;
}
bool FreeType::ReleaseFace(FontFaceHandleFreetype in_face)
{
FT_Face face = (FT_Face)in_face;
FT_Error error = FT_Done_Face(face);
return (error == 0);
}
void FreeType::GetFaceStyle(FontFaceHandleFreetype in_face, String* font_family, Style::FontStyle* style, Style::FontWeight* weight)
{
FT_Face face = (FT_Face)in_face;
if (font_family)
*font_family = face->family_name;
if (style)
*style = face->style_flags & FT_STYLE_FLAG_ITALIC ? Style::FontStyle::Italic : Style::FontStyle::Normal;
if (weight)
{
TT_OS2* font_table = (TT_OS2*)FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
if (font_table && font_table->usWeightClass != 0)
*weight = (Style::FontWeight)font_table->usWeightClass;
else
*weight = (face->style_flags & FT_STYLE_FLAG_BOLD) == FT_STYLE_FLAG_BOLD ? Style::FontWeight::Bold : Style::FontWeight::Normal;
}
}
bool FreeType::InitialiseFaceHandle(FontFaceHandleFreetype face, int font_size, FontGlyphMap& glyphs, FontMetrics& metrics, bool load_default_glyphs)
{
FT_Face ft_face = (FT_Face)face;
metrics.size = font_size;
float bitmap_scaling_factor = 1.0f;
if (!SetFontSize(ft_face, font_size, bitmap_scaling_factor))
return false;
// Construct the initial list of glyphs.
BuildGlyphMap(ft_face, font_size / 4, glyphs, bitmap_scaling_factor, load_default_glyphs);
// Generate the metrics for the handle.
GenerateMetrics(ft_face, metrics, bitmap_scaling_factor);
return true;
}
bool FreeType::AppendGlyph(FontFaceHandleFreetype face, int font_size, Character character, FontGlyphMap& glyphs)
{
FT_Face ft_face = (FT_Face)face;
RMLUI_ASSERT(glyphs.find(character) == glyphs.end());
RMLUI_ASSERT(ft_face);
// Set face size again in case it was used at another size in another font face handle.
float bitmap_scaling_factor = 1.0f;
if (!SetFontSize(ft_face, font_size, bitmap_scaling_factor))
return false;
if (!BuildGlyph(ft_face, character, glyphs, bitmap_scaling_factor))
return false;
return true;
}
int FreeType::GetKerning(FontFaceHandleFreetype face, int font_size, Character lhs, Character rhs)
{
FT_Face ft_face = (FT_Face)face;
RMLUI_ASSERT(FT_HAS_KERNING(ft_face));
// Set face size again in case it was used at another size in another font face handle.
// Font size value of zero assumes it is already set.
if (font_size > 0)
{
float bitmap_scaling_factor = 1.0f;
if (!SetFontSize(ft_face, font_size, bitmap_scaling_factor) || bitmap_scaling_factor != 1.0f)
return 0;
}
FT_Vector ft_kerning;
FT_Error ft_error = FT_Get_Kerning(ft_face, FT_Get_Char_Index(ft_face, (FT_ULong)lhs), FT_Get_Char_Index(ft_face, (FT_ULong)rhs),
FT_KERNING_DEFAULT, &ft_kerning);
if (ft_error)
return 0;
int kerning = ft_kerning.x >> 6;
return kerning;
}
bool FreeType::HasKerning(FontFaceHandleFreetype face)
{
FT_Face ft_face = (FT_Face)face;
return FT_HAS_KERNING(ft_face);
}
static void BuildGlyphMap(FT_Face ft_face, int size, FontGlyphMap& glyphs, const float bitmap_scaling_factor, const bool load_default_glyphs)
{
if (load_default_glyphs)
{
glyphs.reserve(128);
// Add the ASCII characters now. Other characters are added later as needed.
FT_ULong code_min = 32;
FT_ULong code_max = 126;
for (FT_ULong character_code = code_min; character_code <= code_max; ++character_code)
BuildGlyph(ft_face, (Character)character_code, glyphs, bitmap_scaling_factor);
}
// Add a replacement character for rendering unknown characters.
Character replacement_character = Character::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_owned_data.reset(new byte[glyph.bitmap_dimensions.x * glyph.bitmap_dimensions.y]);
glyph.bitmap_data = glyph.bitmap_owned_data.get();
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_owned_data[i] = (near_edge ? 0xdd : 0);
}
}
glyphs[replacement_character] = std::move(glyph);
}
}
static bool BuildGlyph(FT_Face ft_face, const Character character, FontGlyphMap& glyphs, const float bitmap_scaling_factor)
{
FT_UInt index = FT_Get_Char_Index(ft_face, (FT_ULong)character);
if (index == 0)
return false;
FT_Error error = FT_Load_Glyph(ft_face, index, FT_LOAD_COLOR);
if (error != 0)
{
Log::Message(Log::LT_WARNING, "Unable to load glyph for character '%u' on the font face '%s %s'; error code: %d.", (unsigned int)character,
ft_face->family_name, ft_face->style_name, error);
return false;
}
error = FT_Render_Glyph(ft_face->glyph, FT_RENDER_MODE_NORMAL);
if (error != 0)
{
Log::Message(Log::LT_WARNING, "Unable to render glyph for character '%u' on the font face '%s %s'; error code: %d.", (unsigned int)character,
ft_face->family_name, ft_face->style_name, error);
return false;
}
auto result = glyphs.emplace(character, FontGlyph{});
if (!result.second)
{
Log::Message(Log::LT_WARNING, "Glyph character '%u' is already loaded in the font face '%s %s'.", (unsigned int)character,
ft_face->family_name, ft_face->style_name);
return false;
}
FontGlyph& glyph = result.first->second;
FT_GlyphSlot ft_glyph = ft_face->glyph;
// Set the glyph's dimensions.
glyph.dimensions.x = (ft_glyph->metrics.width * global_font_scale) >> 6;
glyph.dimensions.y = (ft_glyph->metrics.height * global_font_scale) >> 6;
// Set the glyph's bearing.
glyph.bearing.x = (ft_glyph->metrics.horiBearingX * global_font_scale) >> 6;
glyph.bearing.y = (ft_glyph->metrics.horiBearingY * global_font_scale) >> 6;
// Set the glyph's advance.
glyph.advance = (ft_glyph->metrics.horiAdvance * global_font_scale) >> 6;
// Set the glyph's bitmap dimensions.
glyph.bitmap_dimensions.x = ft_glyph->bitmap.width;
glyph.bitmap_dimensions.y = ft_glyph->bitmap.rows;
// Determine new metrics if we need to scale the bitmap received from FreeType. Only allow bitmap downscaling.
const bool scale_bitmap = (bitmap_scaling_factor < 1.f);
if (scale_bitmap)
{
glyph.dimensions = Vector2i(Vector2f(glyph.dimensions) * bitmap_scaling_factor);
glyph.bearing = Vector2i(Vector2f(glyph.bearing) * bitmap_scaling_factor);
glyph.advance = int(float(glyph.advance) * bitmap_scaling_factor);
glyph.bitmap_dimensions = Vector2i(Vector2f(glyph.bitmap_dimensions) * bitmap_scaling_factor);
}
// Copy the glyph's bitmap data from the FreeType glyph handle to our glyph handle.
if (glyph.bitmap_dimensions.x * glyph.bitmap_dimensions.y != 0)
{
// Check if the pixel mode is supported.
if (ft_glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO && ft_glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY &&
ft_glyph->bitmap.pixel_mode != FT_PIXEL_MODE_BGRA)
{
Log::Message(Log::LT_WARNING, "Unable to render glyph on the font face '%s %s': unsupported pixel mode (%d).",
ft_glyph->face->family_name, ft_glyph->face->style_name, ft_glyph->bitmap.pixel_mode);
}
else if (ft_glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && scale_bitmap)
{
Log::Message(Log::LT_WARNING, "Unable to render glyph on the font face '%s %s': bitmap scaling unsupported in mono pixel mode.",
ft_glyph->face->family_name, ft_glyph->face->style_name);
}
else
{
const int num_bytes_per_pixel = (ft_glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 1);
glyph.color_format = (ft_glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? ColorFormat::RGBA8 : ColorFormat::A8);
glyph.bitmap_owned_data.reset(new byte[glyph.bitmap_dimensions.x * glyph.bitmap_dimensions.y * num_bytes_per_pixel]);
glyph.bitmap_data = glyph.bitmap_owned_data.get();
byte* destination_bitmap = glyph.bitmap_owned_data.get();
const byte* source_bitmap = ft_glyph->bitmap.buffer;
// Copy the bitmap data into the newly-allocated space on our glyph.
switch (ft_glyph->bitmap.pixel_mode)
{
case FT_PIXEL_MODE_MONO:
{
// Unpack 1-bit data into 8-bit.
for (int i = 0; i < glyph.bitmap_dimensions.y; ++i)
{
int mask = 0x80;
const byte* source_byte = source_bitmap;
for (int j = 0; j < glyph.bitmap_dimensions.x; ++j)
{
if ((*source_byte & mask) == mask)
destination_bitmap[j] = 255;
else
destination_bitmap[j] = 0;
mask >>= 1;
if (mask <= 0)
{
mask = 0x80;
++source_byte;
}
}
destination_bitmap += glyph.bitmap_dimensions.x;
source_bitmap += ft_glyph->bitmap.pitch;
}
}
break;
case FT_PIXEL_MODE_GRAY:
case FT_PIXEL_MODE_BGRA:
{
if (scale_bitmap)
{
// Resize the glyph data to the new dimensions.
BitmapDownscale(destination_bitmap, glyph.bitmap_dimensions.x, glyph.bitmap_dimensions.y, source_bitmap,
(int)ft_glyph->bitmap.width, (int)ft_glyph->bitmap.rows, ft_glyph->bitmap.pitch, glyph.color_format);
}
else
{
// Copy the glyph data directly.
const int num_bytes_per_line = glyph.bitmap_dimensions.x * num_bytes_per_pixel;
for (int i = 0; i < glyph.bitmap_dimensions.y; ++i)
{
memcpy(destination_bitmap, source_bitmap, num_bytes_per_line);
destination_bitmap += num_bytes_per_line;
source_bitmap += ft_glyph->bitmap.pitch;
}
}
if (glyph.color_format == ColorFormat::RGBA8)
{
// Swizzle channels (BGRA -> RGBA) and un-premultiply alpha.
destination_bitmap = glyph.bitmap_owned_data.get();
for (int k = 0; k < glyph.bitmap_dimensions.x * glyph.bitmap_dimensions.y * num_bytes_per_pixel; k += 4)
{
byte b = destination_bitmap[k];
byte g = destination_bitmap[k + 1];
byte r = destination_bitmap[k + 2];
const byte alpha = destination_bitmap[k + 3];
RMLUI_ASSERTMSG(b <= alpha && g <= alpha && r <= alpha, "Assumption of glyph data being premultiplied is broken.");
if (alpha > 0 && alpha < 255)
{
b = byte((b * 255) / alpha);
g = byte((g * 255) / alpha);
r = byte((r * 255) / alpha);
}
destination_bitmap[k] = r;
destination_bitmap[k + 1] = g;
destination_bitmap[k + 2] = b;
destination_bitmap[k + 3] = alpha;
}
}
}
break;
}
}
}
return true;
}
static void GenerateMetrics(FT_Face ft_face, FontMetrics& metrics, float bitmap_scaling_factor)
{
metrics.ascent = ft_face->size->metrics.ascender * bitmap_scaling_factor * global_font_scale / float(1 << 6);
metrics.descent = -ft_face->size->metrics.descender * bitmap_scaling_factor * global_font_scale / float(1 << 6);
metrics.line_spacing = ft_face->size->metrics.height * bitmap_scaling_factor * global_font_scale / float(1 << 6);
metrics.underline_position = FT_MulFix(-ft_face->underline_position, ft_face->size->metrics.y_scale) * bitmap_scaling_factor * global_font_scale / float(1 << 6);
metrics.underline_thickness = FT_MulFix(ft_face->underline_thickness, ft_face->size->metrics.y_scale) * bitmap_scaling_factor * global_font_scale / float(1 << 6);
metrics.underline_thickness = Math::Max(metrics.underline_thickness, 1.0f);
// Determine the x-height of this font face.
FT_UInt index = FT_Get_Char_Index(ft_face, 'x');
if (index != 0 && FT_Load_Glyph(ft_face, index, 0) == 0)
metrics.x_height = ft_face->glyph->metrics.height * bitmap_scaling_factor * global_font_scale / float(1 << 6);
else
metrics.x_height = 0.5f * metrics.line_spacing;
}
static bool SetFontSize(FT_Face ft_face, int font_size, float& out_bitmap_scaling_factor)
{
RMLUI_ASSERT(out_bitmap_scaling_factor == 1.f);
font_size /= global_font_scale;
FT_Error error = 0;
// Set the character size on the font face.
error = FT_Set_Char_Size(ft_face, 0, font_size << 6, 0, 0);
// If setting char size fails, try to select a bitmap strike instead when available.
if (error != 0 && FT_HAS_FIXED_SIZES(ft_face))
{
constexpr int a_big_number = INT_MAX / 2;
int heuristic_min = INT_MAX;
int index_min = -1;
// Select the bitmap strike with the smallest size *above* font_size, or else the largest size.
for (int i = 0; i < ft_face->num_fixed_sizes; i++)
{
const int size_diff = ft_face->available_sizes[i].height - font_size;
const int heuristic = (size_diff < 0 ? a_big_number - size_diff : size_diff);
if (heuristic < heuristic_min)
{
index_min = i;
heuristic_min = heuristic;
}
}
if (index_min >= 0)
{
out_bitmap_scaling_factor = float(font_size) / ft_face->available_sizes[index_min].height;
// Add some tolerance to the scaling factor to avoid unnecessary scaling. Only allow downscaling.
constexpr float bitmap_scaling_factor_threshold = 0.95f;
if (out_bitmap_scaling_factor >= bitmap_scaling_factor_threshold)
out_bitmap_scaling_factor = 1.f;
error = FT_Select_Size(ft_face, index_min);
}
}
if (error != 0)
{
Log::Message(Log::LT_ERROR, "Unable to set the character size '%d' on the font face '%s %s'.", font_size, ft_face->family_name,
ft_face->style_name);
return false;
}
return true;
}
static void BitmapDownscale(byte* bitmap_new, const int new_width, const int new_height, const byte* bitmap_source, const int width, const int height,
const int pitch, const ColorFormat color_format)
{
// Average filter for downscaling bitmap images, based on https://stackoverflow.com/a/9571580
constexpr int max_num_channels = 4;
const int num_channels = (color_format == ColorFormat::RGBA8 ? 4 : 1);
const float xscale = float(new_width) / width;
const float yscale = float(new_height) / height;
const float sumscale = xscale * yscale;
float yend = 0.0f;
for (int f = 0; f < new_height; f++) // y on output
{
const float ystart = yend;
yend = (f + 1) * (1.f / yscale);
if (yend >= height)
yend = height - 0.001f;
float xend = 0.0;
for (int g = 0; g < new_width; g++) // x on output
{
float xstart = xend;
xend = (g + 1) * (1.f / xscale);
if (xend >= width)
xend = width - 0.001f;
float sum[max_num_channels] = {};
for (int y = (int)ystart; y <= (int)yend; ++y)
{
float yportion = 1.0f;
if (y == (int)ystart)
yportion -= ystart - y;
if (y == (int)yend)
yportion -= y + 1 - yend;
for (int x = (int)xstart; x <= (int)xend; ++x)
{
float xportion = 1.0f;
if (x == (int)xstart)
xportion -= xstart - x;
if (x == (int)xend)
xportion -= x + 1 - xend;
for (int i = 0; i < num_channels; i++)
sum[i] += bitmap_source[y * pitch + x * num_channels + i] * yportion * xportion;
}
}
for (int i = 0; i < num_channels; i++)
bitmap_new[(f * new_width + g) * num_channels + i] = (byte)Math::Min(sum[i] * sumscale, 255.f);
}
}
}
} // namespace Rml

View File

@ -1,73 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#ifndef RMLUI_CORE_FONTENGINESCALED_FREETYPEINTERFACE_H
#define RMLUI_CORE_FONTENGINESCALED_FREETYPEINTERFACE_H
#include "RmlUi/Core/FontMetrics.h"
#include "FontTypes.h"
namespace RecompRml {
using namespace Rml;
namespace FreeType {
// Initialize FreeType library.
bool Initialise();
// Shutdown FreeType library.
void Shutdown();
// Returns a sorted list of available font variations for the font face located in memory.
bool GetFaceVariations(const byte* data, int data_length, Vector<FaceVariation>& out_face_variations);
// Loads a FreeType face from memory, 'source' is only used for logging.
FontFaceHandleFreetype LoadFace(const byte* data, int data_length, const String& source, int named_instance_index = 0);
// Releases the FreeType face.
bool ReleaseFace(FontFaceHandleFreetype face);
// Retrieves the font family, style and weight of the given font face. Use nullptr to ignore a property.
void GetFaceStyle(FontFaceHandleFreetype face, String* font_family, Style::FontStyle* style, Style::FontWeight* weight);
// Initializes a face for a given font size. Glyphs are filled with the ASCII subset, and the font face metrics are set.
bool InitialiseFaceHandle(FontFaceHandleFreetype face, int font_size, FontGlyphMap& glyphs, FontMetrics& metrics, bool load_default_glyphs);
// Build a new glyph representing the given code point and append to 'glyphs'.
bool AppendGlyph(FontFaceHandleFreetype face, int font_size, Character character, FontGlyphMap& glyphs);
// Returns the kerning between two characters.
// 'font_size' value of zero assumes the font size is already set on the face, and skips this step for performance reasons.
int GetKerning(FontFaceHandleFreetype face, int font_size, Character lhs, Character rhs);
// Returns true if the font face has kerning.
bool HasKerning(FontFaceHandleFreetype face);
} // namespace FreeType
} // namespace Rml
#endif

View File

@ -1,216 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#include "ElementScaledSVG.h"
#include "../FontEngineScaled/FontTypes.h"
#include "RmlUi/Core/ComputedValues.h"
#include "RmlUi/Core/Core.h"
#include "RmlUi/Core/ElementDocument.h"
#include "RmlUi/Core/FileInterface.h"
#include "RmlUi/Core/GeometryUtilities.h"
#include "RmlUi/Core/Math.h"
#include "RmlUi/Core/PropertyIdSet.h"
#include "RmlUi/Core/RenderInterface.h"
#include "RmlUi/Core/SystemInterface.h"
#include <cmath>
#include <lunasvg.h>
#include <string.h>
namespace RecompRml {
using namespace Rml;
ElementScaledSVG::ElementScaledSVG(const String& tag) : Element(tag) {}
ElementScaledSVG::~ElementScaledSVG() {}
bool ElementScaledSVG::GetIntrinsicDimensions(Vector2f& dimensions, float& ratio)
{
if (source_dirty)
LoadSource();
dimensions = intrinsic_dimensions;
if (HasAttribute("width"))
{
dimensions.x = GetAttribute<float>("width", -1);
}
if (HasAttribute("height"))
{
dimensions.y = GetAttribute<float>("height", -1);
}
if (dimensions.y > 0)
ratio = dimensions.x / dimensions.y;
return true;
}
void ElementScaledSVG::OnRender()
{
if (svg_document)
{
if (geometry_dirty)
GenerateGeometry();
UpdateTexture();
geometry.Render(GetAbsoluteOffset(BoxArea::Content));
}
}
void ElementScaledSVG::OnResize()
{
geometry_dirty = true;
texture_dirty = true;
}
void ElementScaledSVG::OnAttributeChange(const ElementAttributes& changed_attributes)
{
Element::OnAttributeChange(changed_attributes);
if (changed_attributes.count("src"))
{
source_dirty = true;
DirtyLayout();
}
if (changed_attributes.find("width") != changed_attributes.end() || changed_attributes.find("height") != changed_attributes.end())
{
DirtyLayout();
}
}
void ElementScaledSVG::OnPropertyChange(const PropertyIdSet& changed_properties)
{
Element::OnPropertyChange(changed_properties);
if (changed_properties.Contains(PropertyId::ImageColor) || changed_properties.Contains(PropertyId::Opacity))
{
geometry_dirty = true;
}
}
void ElementScaledSVG::GenerateGeometry()
{
geometry.Release(true);
Vector<Vertex>& vertices = geometry.GetVertices();
Vector<int>& indices = geometry.GetIndices();
vertices.resize(4);
indices.resize(6);
Vector2f texcoords[2] = {
{0.0f, 0.0f},
{1.0f, 1.0f},
};
const ComputedValues& computed = GetComputedValues();
const float opacity = computed.opacity();
Colourb quad_colour = computed.image_color();
quad_colour.alpha = (byte)(opacity * (float)quad_colour.alpha);
const Vector2f render_dimensions_f = GetBox().GetSize(BoxArea::Content).Round();
render_dimensions.x = int(render_dimensions_f.x) / RecompRml::global_font_scale;
render_dimensions.y = int(render_dimensions_f.y) / RecompRml::global_font_scale;
GeometryUtilities::GenerateQuad(&vertices[0], &indices[0], Vector2f(0, 0), render_dimensions_f, quad_colour, texcoords[0], texcoords[1]);
geometry_dirty = false;
}
bool ElementScaledSVG::LoadSource()
{
source_dirty = false;
texture_dirty = true;
intrinsic_dimensions = Vector2f{};
geometry.SetTexture(nullptr);
svg_document.reset();
const String attribute_src = GetAttribute<String>("src", "");
if (attribute_src.empty())
return false;
String path = attribute_src;
String directory;
if (ElementDocument* document = GetOwnerDocument())
{
const String document_source_url = StringUtilities::Replace(document->GetSourceURL(), '|', ':');
GetSystemInterface()->JoinPath(path, document_source_url, attribute_src);
GetSystemInterface()->JoinPath(directory, document_source_url, "");
}
String svg_data;
if (path.empty() || !GetFileInterface()->LoadFile(path, svg_data))
{
Log::Message(Rml::Log::Type::LT_WARNING, "Could not load SVG file %s", path.c_str());
return false;
}
// We use a reset-release approach here in case clients use a non-std unique_ptr (lunasvg uses std::unique_ptr)
svg_document.reset(lunasvg::Document::loadFromData(svg_data).release());
if (!svg_document)
{
Log::Message(Rml::Log::Type::LT_WARNING, "Could not load SVG data from file %s", path.c_str());
return false;
}
intrinsic_dimensions.x = Math::Max(float(svg_document->width()), 1.0f);
intrinsic_dimensions.y = Math::Max(float(svg_document->height()), 1.0f);
return true;
}
void ElementScaledSVG::UpdateTexture()
{
if (!svg_document || !texture_dirty)
return;
// Callback for generating texture.
auto p_callback = [this](RenderInterface* render_interface, const String& /*name*/, TextureHandle& out_handle, Vector2i& out_dimensions) -> bool {
RMLUI_ASSERT(svg_document);
lunasvg::Bitmap bitmap = svg_document->renderToBitmap(render_dimensions.x, render_dimensions.y);
if (!bitmap.valid() || !bitmap.data())
return false;
if (!render_interface->GenerateTexture(out_handle, reinterpret_cast<const Rml::byte*>(bitmap.data()), render_dimensions))
return false;
out_dimensions = render_dimensions;
return true;
};
texture.Set("svg", p_callback);
geometry.SetTexture(&texture);
texture_dirty = false;
}
} // namespace Rml

View File

@ -1,98 +0,0 @@
/*
* This source file is modified from a part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* 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.
*
*/
#ifndef RMLUI_SVG_ELEMENT_SCALEDSVG_H
#define RMLUI_SVG_ELEMENT_SCALEDSVG_H
#include "RmlUi/Core/Element.h"
#include "RmlUi/Core/Geometry.h"
#include "RmlUi/Core/Header.h"
#include "RmlUi/Core/Texture.h"
namespace lunasvg {
class Document;
}
namespace RecompRml {
using namespace Rml;
class RMLUICORE_API ElementScaledSVG : public Element {
public:
RMLUI_RTTI_DefineWithParent(ElementScaledSVG, Element)
ElementScaledSVG(const String& tag);
virtual ~ElementScaledSVG();
/// Returns the element's inherent size.
bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;
protected:
/// Renders the image.
void OnRender() override;
/// Regenerates the element's geometry.
void OnResize() override;
/// Checks for changes to the image's source or dimensions.
/// @param[in] changed_attributes A list of attributes changed on the element.
void OnAttributeChange(const ElementAttributes& changed_attributes) override;
/// Called when properties on the element are changed.
/// @param[in] changed_properties The properties changed on the element.
void OnPropertyChange(const PropertyIdSet& changed_properties) override;
private:
// Generates the element's geometry.
void GenerateGeometry();
// Loads the SVG document specified by the 'src' attribute.
bool LoadSource();
// Update the texture when necessary.
void UpdateTexture();
bool source_dirty = false;
bool geometry_dirty = false;
bool texture_dirty = false;
// The texture this element is rendering from.
Texture texture;
// The image's intrinsic dimensions.
Vector2f intrinsic_dimensions;
// The element's size for rendering.
Vector2i render_dimensions;
// The geometry used to render this element.
Geometry geometry;
UniquePtr<lunasvg::Document> svg_document;
};
} // namespace Rml
#endif

View File

@ -4,6 +4,8 @@
#include "recomp_ui.h"
#include <string.h>
using ColourMap = Rml::UnorderedMap<Rml::String, Rml::Colourb>;
namespace recomp {
class PropertyParserColorHack : public Rml::PropertyParser {
public:
@ -11,8 +13,7 @@ namespace recomp {
virtual ~PropertyParserColorHack();
bool ParseValue(Rml::Property& property, const Rml::String& value, const Rml::ParameterMap& /*parameters*/) const override;
private:
using ColourMap = Rml::UnorderedMap<Rml::String, Rml::Colourb>;
ColourMap html_colours;
static ColourMap html_colours;
};
static_assert(sizeof(PropertyParserColorHack) == sizeof(Rml::PropertyParserColour));
PropertyParserColorHack::PropertyParserColorHack() {
@ -165,4 +166,6 @@ namespace recomp {
// Copy the allocated object into the color parser pointer to overwrite its vtable.
memcpy((void*)Rml::StyleSheetSpecification::GetParser("color"), (void*)new_parser, sizeof(*new_parser));
}
ColourMap PropertyParserColorHack::html_colours{};
}

View File

@ -18,6 +18,7 @@
#include "RmlUi/Core.h"
#include "RmlUi/Debugger.h"
#include "RmlUi/Core/RenderInterfaceCompatibility.h"
#include "RmlUi/../../Source/Core/Elements/ElementLabel.h"
#include "RmlUi_Platform_SDL.h"
@ -29,10 +30,6 @@
# include "InterfacePS.hlsl.dxil.h"
#endif
#include "FontEngineScaled/FontEngineInterfaceScaled.h"
#include "FontEngineScaled/FontTypes.h"
#include "ScaledSVG/ElementScaledSVG.h"
#ifdef _WIN32
# define GET_SHADER_BLOB(name, format) \
((format) == RT64::RenderShaderFormat::SPIRV ? name##BlobSPIRV : \
@ -98,7 +95,7 @@ T from_bytes_le(const char* input) {
void load_document();
class RmlRenderInterface_RT64 : public Rml::RenderInterface {
class RmlRenderInterface_RT64 : public Rml::RenderInterfaceCompatibility {
static constexpr uint32_t per_frame_descriptor_set = 0;
static constexpr uint32_t per_draw_descriptor_set = 1;
@ -246,7 +243,7 @@ public:
screen_vertex_buffer_size_ = sizeof(Rml::Vertex) * 3;
screen_vertex_buffer_ = render_context->device->createBuffer(RT64::RenderBufferDesc::VertexBuffer(screen_vertex_buffer_size_, RT64::RenderHeapType::UPLOAD));
Rml::Vertex *vertices = (Rml::Vertex *)(screen_vertex_buffer_->map());
const Rml::Colourb white(255, 255, 255, 255);
const Rml::ColourbPremultiplied white(255, 255, 255, 255);
vertices[0] = Rml::Vertex{ Rml::Vector2f(-1.0f, 1.0f), white, Rml::Vector2f(0.0f, 0.0f) };
vertices[1] = Rml::Vertex{ Rml::Vector2f(-1.0f, -3.0f), white, Rml::Vector2f(0.0f, 2.0f) };
vertices[2] = Rml::Vertex{ Rml::Vector2f(3.0f, 1.0f), white, Rml::Vector2f(2.0f, 0.0f) };
@ -384,10 +381,10 @@ public:
list_->setViewports(RT64::RenderViewport{ 0, 0, float(window_width_), float(window_height_) });
if (scissor_enabled_) {
list_->setScissors(RT64::RenderRect{
scissor_x_ / RecompRml::global_font_scale,
scissor_y_ / RecompRml::global_font_scale,
(scissor_width_ + scissor_x_) / RecompRml::global_font_scale,
(scissor_height_ + scissor_y_) / RecompRml::global_font_scale });
scissor_x_,
scissor_y_,
(scissor_width_ + scissor_x_),
(scissor_height_ + scissor_y_) });
}
else {
list_->setScissors(RT64::RenderRect{ 0, 0, window_width_, window_height_ });
@ -610,7 +607,7 @@ public:
window_width_ = image_width;
window_height_ = image_height;
projection_mtx_ = Rml::Matrix4f::ProjectOrtho(0.0f, float(image_width * RecompRml::global_font_scale), float(image_height * RecompRml::global_font_scale), 0.0f, -10000, 10000);
projection_mtx_ = Rml::Matrix4f::ProjectOrtho(0.0f, float(image_width), float(image_height), 0.0f, -10000, 10000);
recalculate_mvp();
// The following code assumes command lists aren't double buffered.
@ -781,8 +778,6 @@ struct UIContext {
int last_active_mouse_position[2] = {0, 0};
std::unique_ptr<SystemInterface_SDL> system_interface;
std::unique_ptr<RmlRenderInterface_RT64> render_interface;
std::unique_ptr<Rml::FontEngineInterface> font_interface;
std::unique_ptr<Rml::ElementInstancer> svg_instancer;
Rml::Context* context;
recomp::UiEventListenerInstancer event_listener_instancer;
@ -1019,17 +1014,10 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
ui_context->rml.make_event_listeners();
Rml::SetSystemInterface(ui_context->rml.system_interface.get());
Rml::SetRenderInterface(ui_context->rml.render_interface.get());
Rml::SetRenderInterface(ui_context->rml.render_interface.get()->GetAdaptedInterface());
Rml::Factory::RegisterEventListenerInstancer(&ui_context->rml.event_listener_instancer);
ui_context->rml.font_interface = std::make_unique<RecompRml::FontEngineInterfaceScaled>();
Rml::SetFontEngineInterface(ui_context->rml.font_interface.get());
Rml::Initialise();
ui_context->rml.svg_instancer = std::make_unique<Rml::ElementInstancerGeneric<RecompRml::ElementScaledSVG>>();
Rml::Factory::RegisterElementInstancer("svg", ui_context->rml.svg_instancer.get());
// Apply the hack to replace RmlUi's default color parser with one that conforms to HTML5 alpha parsing for SASS compatibility
recomp::apply_color_hack();
@ -1037,7 +1025,7 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
int width, height;
SDL_GetWindowSizeInPixels(window, &width, &height);
ui_context->rml.context = Rml::CreateContext("main", Rml::Vector2i(width * RecompRml::global_font_scale, height * RecompRml::global_font_scale));
ui_context->rml.context = Rml::CreateContext("main", Rml::Vector2i(width, height));
ui_context->rml.make_bindings();
Rml::Debugger::Initialise(ui_context->rml.context);
@ -1164,31 +1152,6 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
while (recomp::try_deque_event(cur_event)) {
bool menu_is_open = cur_menu != recomp::Menu::None;
// Scale coordinates for mouse and window events based on the UI scale
switch (cur_event.type) {
case SDL_EventType::SDL_MOUSEMOTION:
cur_event.motion.x *= RecompRml::global_font_scale;
cur_event.motion.y *= RecompRml::global_font_scale;
cur_event.motion.xrel *= RecompRml::global_font_scale;
cur_event.motion.yrel *= RecompRml::global_font_scale;
break;
case SDL_EventType::SDL_MOUSEBUTTONDOWN:
case SDL_EventType::SDL_MOUSEBUTTONUP:
cur_event.button.x *= RecompRml::global_font_scale;
cur_event.button.y *= RecompRml::global_font_scale;
break;
case SDL_EventType::SDL_MOUSEWHEEL:
cur_event.wheel.x *= RecompRml::global_font_scale;
cur_event.wheel.y *= RecompRml::global_font_scale;
break;
case SDL_EventType::SDL_WINDOWEVENT:
if (cur_event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
cur_event.window.data1 *= RecompRml::global_font_scale;
cur_event.window.data2 *= RecompRml::global_font_scale;
}
break;
}
if (!recomp::all_input_disabled()) {
// Implement some additional behavior for specific events on top of what RmlUi normally does with them.
switch (cur_event.type) {
@ -1298,7 +1261,7 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
int height = swap_chain_framebuffer->getHeight();
// Scale the UI based on the window size with 1080 vertical resolution as the reference point.
ui_context->rml.context->SetDensityIndependentPixelRatio((height * RecompRml::global_font_scale) / 1080.0f);
ui_context->rml.context->SetDensityIndependentPixelRatio((height) / 1080.0f);
ui_context->rml.render_interface->start(command_list, width, height);
@ -1306,7 +1269,7 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
static int prev_height = 0;
if (prev_width != width || prev_height != height) {
ui_context->rml.context->SetDimensions({ (int)(width * RecompRml::global_font_scale), (int)(height * RecompRml::global_font_scale) });
ui_context->rml.context->SetDimensions({ width, height });
}
prev_width = width;
prev_height = height;
@ -1339,7 +1302,6 @@ void recomp::set_config_submenu(recomp::ConfigSubmenu submenu) {
void recomp::destroy_ui() {
std::lock_guard lock {ui_context_mutex};
Rml::Debugger::Shutdown();
ui_context->rml.font_interface.reset();
Rml::Shutdown();
ui_context->rml.unload();
ui_context.reset();