mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2024-11-05 22:35:06 +01:00
Added custom font interface that scales font glyphs down to avoid downsampling
This commit is contained in:
parent
3456b03b4d
commit
d6e4e98c8b
@ -128,6 +128,14 @@ set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.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}/rsp/aspMain.cpp
|
||||
${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.cpp
|
||||
|
||||
@ -146,6 +154,7 @@ target_include_directories(MMRecomp PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src
|
||||
${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/rhi
|
||||
${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/render
|
||||
${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/include
|
||||
${CMAKE_BINARY_DIR}/shaders
|
||||
)
|
||||
|
||||
|
100
src/ui/FontEngineScaled/FontEngineInterfaceScaled.cpp
Normal file
100
src/ui/FontEngineScaled/FontEngineInterfaceScaled.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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
|
75
src/ui/FontEngineScaled/FontEngineInterfaceScaled.h
Normal file
75
src/ui/FontEngineScaled/FontEngineInterfaceScaled.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This source file is modified from a part, 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
|
95
src/ui/FontEngineScaled/FontFace.cpp
Normal file
95
src/ui/FontEngineScaled/FontFace.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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
|
74
src/ui/FontEngineScaled/FontFace.h
Normal file
74
src/ui/FontEngineScaled/FontFace.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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
|
487
src/ui/FontEngineScaled/FontFaceHandleScaled.cpp
Normal file
487
src/ui/FontEngineScaled/FontFaceHandleScaled.cpp
Normal file
@ -0,0 +1,487 @@
|
||||
/*
|
||||
* 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
|
157
src/ui/FontEngineScaled/FontFaceHandleScaled.h
Normal file
157
src/ui/FontEngineScaled/FontFaceHandleScaled.h
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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
|
275
src/ui/FontEngineScaled/FontFaceLayer.cpp
Normal file
275
src/ui/FontEngineScaled/FontFaceLayer.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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
|
138
src/ui/FontEngineScaled/FontFaceLayer.h
Normal file
138
src/ui/FontEngineScaled/FontFaceLayer.h
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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
|
97
src/ui/FontEngineScaled/FontFamily.cpp
Normal file
97
src/ui/FontEngineScaled/FontFamily.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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
|
82
src/ui/FontEngineScaled/FontFamily.h
Normal file
82
src/ui/FontEngineScaled/FontFamily.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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
|
268
src/ui/FontEngineScaled/FontProvider.cpp
Normal file
268
src/ui/FontEngineScaled/FontProvider.cpp
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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
|
102
src/ui/FontEngineScaled/FontProvider.h
Normal file
102
src/ui/FontEngineScaled/FontProvider.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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
|
58
src/ui/FontEngineScaled/FontTypes.h
Normal file
58
src/ui/FontEngineScaled/FontTypes.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 = 4;
|
||||
|
||||
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
|
602
src/ui/FontEngineScaled/FreeTypeInterface.cpp
Normal file
602
src/ui/FontEngineScaled/FreeTypeInterface.cpp
Normal file
@ -0,0 +1,602 @@
|
||||
/*
|
||||
* 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
|
73
src/ui/FontEngineScaled/FreeTypeInterface.h
Normal file
73
src/ui/FontEngineScaled/FreeTypeInterface.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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
|
@ -26,6 +26,8 @@
|
||||
# include "InterfacePS.hlsl.dxil.h"
|
||||
#endif
|
||||
|
||||
#include "FontEngineScaled/FontEngineInterfaceScaled.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# define GET_SHADER_BLOB(name, format) \
|
||||
((format) == RT64::RenderShaderFormat::SPIRV ? name##BlobSPIRV : \
|
||||
@ -735,6 +737,7 @@ struct {
|
||||
public:
|
||||
std::unique_ptr<SystemInterface_SDL> system_interface;
|
||||
std::unique_ptr<RmlRenderInterface_RT64> render_interface;
|
||||
std::unique_ptr<Rml::FontEngineInterface> font_interface;
|
||||
Rml::Context* context;
|
||||
recomp::UiEventListenerInstancer event_listener_instancer;
|
||||
int32_t ui_scale = 4;
|
||||
@ -887,6 +890,9 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
||||
Rml::SetSystemInterface(UIContext.rml.system_interface.get());
|
||||
Rml::SetRenderInterface(UIContext.rml.render_interface.get());
|
||||
Rml::Factory::RegisterEventListenerInstancer(&UIContext.rml.event_listener_instancer);
|
||||
|
||||
UIContext.rml.font_interface = std::make_unique<RecompRml::FontEngineInterfaceScaled>();
|
||||
Rml::SetFontEngineInterface(UIContext.rml.font_interface.get());
|
||||
|
||||
Rml::Initialise();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user