Skip to content

Commit 66d0b7c

Browse files
committed
Add text decorator, solves #679
1 parent 1a34877 commit 66d0b7c

11 files changed

+465
-31
lines changed

Include/RmlUi/Core/FontEngineInterface.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class RMLUICORE_API FontEngineInterface {
118118

119119
/// Called by RmlUi to determine if the text geometry is required to be re-generated. Whenever the returned version
120120
/// is changed, all geometry belonging to the given face handle will be re-generated.
121-
/// @param[in] face_handle The font handle.
121+
/// @param[in] handle The font handle.
122122
/// @return The version required for using any geometry generated with the face handle.
123123
virtual int GetVersion(FontFaceHandle handle);
124124

Source/Core/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ add_library(rmlui_core
4040
DecoratorNinePatch.h
4141
DecoratorShader.cpp
4242
DecoratorShader.h
43+
DecoratorText.cpp
44+
DecoratorText.h
4345
DecoratorTiled.cpp
4446
DecoratorTiled.h
4547
DecoratorTiledBox.cpp
@@ -50,6 +52,8 @@ add_library(rmlui_core
5052
DecoratorTiledImage.h
5153
DecoratorTiledVertical.cpp
5254
DecoratorTiledVertical.h
55+
DecoratorUtilities.cpp
56+
DecoratorUtilities.h
5357
DocumentHeader.cpp
5458
DocumentHeader.h
5559
EffectSpecification.cpp

Source/Core/DecoratorGradient.cpp

+2-28
Original file line numberDiff line numberDiff line change
@@ -149,32 +149,6 @@ static ColorStopList ResolveColorStops(Element* element, const float gradient_li
149149
return stops;
150150
}
151151

152-
// Compute a 2d-position property value into a percentage-length vector.
153-
static Vector2Numeric ComputePosition(const Property* p_position[2])
154-
{
155-
Vector2Numeric position;
156-
for (int dimension = 0; dimension < 2; dimension++)
157-
{
158-
NumericValue& value = position[dimension];
159-
const Property& property = *p_position[dimension];
160-
if (property.unit == Unit::KEYWORD)
161-
{
162-
enum { TOP_LEFT, CENTER, BOTTOM_RIGHT };
163-
switch (property.Get<int>())
164-
{
165-
case TOP_LEFT: value = NumericValue(0.f, Unit::PERCENT); break;
166-
case CENTER: value = NumericValue(50.f, Unit::PERCENT); break;
167-
case BOTTOM_RIGHT: value = NumericValue(100.f, Unit::PERCENT); break;
168-
}
169-
}
170-
else
171-
{
172-
value = property.GetNumericValue();
173-
}
174-
}
175-
return position;
176-
}
177-
178152
DecoratorStraightGradient::DecoratorStraightGradient() {}
179153

180154
DecoratorStraightGradient::~DecoratorStraightGradient() {}
@@ -596,7 +570,7 @@ SharedPtr<Decorator> DecoratorRadialGradientInstancer::InstanceDecorator(const S
596570
const Property* p_ending_shape = properties_.GetProperty(ids.ending_shape);
597571
const Property* p_size_x = properties_.GetProperty(ids.size_x);
598572
const Property* p_size_y = properties_.GetProperty(ids.size_y);
599-
const Property* p_position[2] = {properties_.GetProperty(ids.position_x), properties_.GetProperty(ids.position_y)};
573+
Array<const Property*, 2> p_position = {properties_.GetProperty(ids.position_x), properties_.GetProperty(ids.position_y)};
600574
const Property* p_color_stop_list = properties_.GetProperty(ids.color_stop_list);
601575

602576
if (!p_ending_shape || !p_size_x || !p_size_y || !p_position[0] || !p_position[1] || !p_color_stop_list)
@@ -727,7 +701,7 @@ SharedPtr<Decorator> DecoratorConicGradientInstancer::InstanceDecorator(const St
727701
const DecoratorInstancerInterface& /*interface_*/)
728702
{
729703
const Property* p_angle = properties_.GetProperty(ids.angle);
730-
const Property* p_position[2] = {properties_.GetProperty(ids.position_x), properties_.GetProperty(ids.position_y)};
704+
Array<const Property*, 2> p_position = {properties_.GetProperty(ids.position_x), properties_.GetProperty(ids.position_y)};
731705
const Property* p_color_stop_list = properties_.GetProperty(ids.color_stop_list);
732706

733707
if (!p_angle || !p_position[0] || !p_position[1] || !p_color_stop_list)

Source/Core/DecoratorGradient.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,10 @@
3333
#include "../../Include/RmlUi/Core/Decorator.h"
3434
#include "../../Include/RmlUi/Core/Geometry.h"
3535
#include "../../Include/RmlUi/Core/ID.h"
36+
#include "DecoratorUtilities.h"
3637

3738
namespace Rml {
3839

39-
using Vector2Numeric = Vector2<NumericValue>;
40-
4140
/**
4241
Straight gradient.
4342

Source/Core/DecoratorText.cpp

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* This source file is part of RmlUi, the HTML/CSS Interface Middleware
3+
*
4+
* For the latest information, see http://github.com/mikke89/RmlUi
5+
*
6+
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
7+
* Copyright (c) 2019-2024 The RmlUi Team, and contributors
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in
17+
* all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*
27+
*/
28+
29+
#include "DecoratorText.h"
30+
#include "../../Include/RmlUi/Core/ComputedValues.h"
31+
#include "../../Include/RmlUi/Core/Context.h"
32+
#include "../../Include/RmlUi/Core/Element.h"
33+
#include "../../Include/RmlUi/Core/FontEngineInterface.h"
34+
#include "../../Include/RmlUi/Core/Geometry.h"
35+
#include "../../Include/RmlUi/Core/PropertyDefinition.h"
36+
#include "../../Include/RmlUi/Core/RenderManager.h"
37+
#include "../../Include/RmlUi/Core/TextShapingContext.h"
38+
39+
namespace Rml {
40+
41+
DecoratorText::DecoratorText() {}
42+
43+
DecoratorText::~DecoratorText() {}
44+
45+
void DecoratorText::Initialise(String in_text, bool in_inherit_color, Colourb in_color, Vector2Numeric in_align)
46+
{
47+
text = std::move(in_text);
48+
inherit_color = in_inherit_color;
49+
color = in_color;
50+
align = in_align;
51+
}
52+
53+
DecoratorDataHandle DecoratorText::GenerateElementData(Element* element, BoxArea paint_area) const
54+
{
55+
ElementData* data = new ElementData{paint_area, {}, -1};
56+
57+
if (!GenerateGeometry(element, *data))
58+
{
59+
Log::Message(Log::LT_WARNING, "Could not construct text decorator with text %s on element %s", text.c_str(), element->GetAddress().c_str());
60+
return {};
61+
}
62+
63+
return reinterpret_cast<DecoratorDataHandle>(data);
64+
}
65+
66+
void DecoratorText::ReleaseElementData(DecoratorDataHandle element_data) const
67+
{
68+
delete reinterpret_cast<ElementData*>(element_data);
69+
}
70+
71+
void DecoratorText::RenderElement(Element* element, DecoratorDataHandle element_data) const
72+
{
73+
ElementData* data = reinterpret_cast<ElementData*>(element_data);
74+
if (!GenerateGeometry(element, *data))
75+
return;
76+
77+
const Vector2f translation = element->GetAbsoluteOffset(BoxArea::Border);
78+
79+
for (size_t i = 0; i < data->textured_geometry.size(); ++i)
80+
data->textured_geometry[i].geometry.Render(translation, data->textured_geometry[i].texture);
81+
}
82+
83+
bool DecoratorText::GenerateGeometry(Element* element, ElementData& element_data) const
84+
{
85+
FontFaceHandle font_face_handle = element->GetFontFaceHandle();
86+
if (font_face_handle == 0)
87+
return false;
88+
89+
FontEngineInterface* font_engine_interface = GetFontEngineInterface();
90+
const int new_version = font_engine_interface->GetVersion(font_face_handle);
91+
if (new_version == element_data.font_handle_version)
92+
return true;
93+
94+
const auto& computed = element->GetComputedValues();
95+
const TextShapingContext text_shaping_context{computed.language(), computed.direction(), computed.letter_spacing()};
96+
97+
const int string_width = font_engine_interface->GetStringWidth(font_face_handle, text, text_shaping_context);
98+
99+
const FontMetrics& metrics = font_engine_interface->GetFontMetrics(font_face_handle);
100+
const RenderBox render_box = element->GetRenderBox(element_data.paint_area);
101+
const Vector2f text_size = {float(string_width), metrics.ascent + metrics.descent};
102+
const Vector2f offset_to_align_area = render_box.GetFillOffset() + Vector2f{0, metrics.ascent};
103+
const Vector2f size_of_align_area = render_box.GetFillSize() - text_size;
104+
const Vector2f offset_within_align_area =
105+
Vector2f{element->ResolveNumericValue(align.x, size_of_align_area.x), element->ResolveNumericValue(align.y, size_of_align_area.y)};
106+
107+
const Vector2f offset = offset_to_align_area + offset_within_align_area;
108+
const float opacity = computed.opacity();
109+
const ColourbPremultiplied text_color = (inherit_color ? computed.color() : color).ToPremultiplied(opacity);
110+
111+
RenderManager& render_manager = element->GetContext()->GetRenderManager();
112+
TexturedMeshList mesh_list;
113+
font_engine_interface->GenerateString(render_manager, font_face_handle, {}, text, offset, text_color, opacity, text_shaping_context, mesh_list);
114+
115+
if (mesh_list.empty())
116+
return false;
117+
118+
Vector<TexturedGeometry> textured_geometry(mesh_list.size());
119+
for (size_t i = 0; i < textured_geometry.size(); i++)
120+
{
121+
textured_geometry[i].geometry = render_manager.MakeGeometry(std::move(mesh_list[i].mesh));
122+
textured_geometry[i].texture = mesh_list[i].texture;
123+
}
124+
125+
element_data = ElementData{
126+
element_data.paint_area,
127+
std::move(textured_geometry),
128+
font_engine_interface->GetVersion(font_face_handle),
129+
};
130+
131+
return true;
132+
}
133+
134+
DecoratorTextInstancer::DecoratorTextInstancer()
135+
{
136+
ids = {};
137+
ids.text = RegisterProperty("text", "").AddParser("string").GetId();
138+
ids.color = RegisterProperty("color", "inherit-color").AddParser("keyword", "inherit-color").AddParser("color").GetId();
139+
ids.align_x = RegisterProperty("align-x", "center").AddParser("keyword", "left, center, right").AddParser("length_percent").GetId();
140+
ids.align_y = RegisterProperty("align-y", "center").AddParser("keyword", "top, center, bottom").AddParser("length_percent").GetId();
141+
142+
RegisterShorthand("decorator", "text, color, align-x, align-y, align-x", ShorthandType::FallThrough);
143+
}
144+
145+
DecoratorTextInstancer::~DecoratorTextInstancer() {}
146+
147+
SharedPtr<Decorator> DecoratorTextInstancer::InstanceDecorator(const String& /*name*/, const PropertyDictionary& properties,
148+
const DecoratorInstancerInterface& /*instancer_interface*/)
149+
{
150+
const Property* p_text = properties.GetProperty(ids.text);
151+
const Property* p_color = properties.GetProperty(ids.color);
152+
Array<const Property*, 2> p_align = {properties.GetProperty(ids.align_x), properties.GetProperty(ids.align_y)};
153+
if (!p_text || !p_color || !p_align[0] || !p_align[1])
154+
return nullptr;
155+
156+
String text = StringUtilities::DecodeRml(p_text->Get<String>());
157+
if (text.empty())
158+
return nullptr;
159+
160+
const bool inherit_color = (p_color->unit == Unit::KEYWORD);
161+
const Colourb color = (p_color->unit == Unit::COLOUR ? p_color->Get<Colourb>() : Colourb{});
162+
const Vector2Numeric align = ComputePosition(p_align);
163+
164+
auto decorator = MakeShared<DecoratorText>();
165+
decorator->Initialise(std::move(text), inherit_color, color, align);
166+
return decorator;
167+
}
168+
169+
} // namespace Rml

Source/Core/DecoratorText.h

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* This source file is part of RmlUi, the HTML/CSS Interface Middleware
3+
*
4+
* For the latest information, see http://github.com/mikke89/RmlUi
5+
*
6+
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
7+
* Copyright (c) 2019-2024 The RmlUi Team, and contributors
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in
17+
* all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*
27+
*/
28+
29+
#ifndef RMLUI_CORE_DECORATORTEXT_H
30+
#define RMLUI_CORE_DECORATORTEXT_H
31+
32+
#include "../../Include/RmlUi/Core/Decorator.h"
33+
#include "../../Include/RmlUi/Core/Geometry.h"
34+
#include "../../Include/RmlUi/Core/ID.h"
35+
#include "DecoratorUtilities.h"
36+
37+
namespace Rml {
38+
39+
class DecoratorText : public Decorator {
40+
public:
41+
DecoratorText();
42+
virtual ~DecoratorText();
43+
44+
void Initialise(String text, bool inherit_color, Colourb color, Vector2Numeric align);
45+
46+
DecoratorDataHandle GenerateElementData(Element* element, BoxArea paint_area) const override;
47+
void ReleaseElementData(DecoratorDataHandle element_data) const override;
48+
49+
void RenderElement(Element* element, DecoratorDataHandle element_data) const override;
50+
51+
private:
52+
struct TexturedGeometry {
53+
Geometry geometry;
54+
Texture texture;
55+
};
56+
struct ElementData {
57+
BoxArea paint_area;
58+
Vector<TexturedGeometry> textured_geometry;
59+
int font_handle_version;
60+
};
61+
62+
bool GenerateGeometry(Element* element, ElementData& element_data) const;
63+
64+
String text;
65+
bool inherit_color = false;
66+
Colourb color;
67+
Vector2Numeric align;
68+
};
69+
70+
class DecoratorTextInstancer : public DecoratorInstancer {
71+
public:
72+
DecoratorTextInstancer();
73+
~DecoratorTextInstancer();
74+
75+
SharedPtr<Decorator> InstanceDecorator(const String& name, const PropertyDictionary& properties,
76+
const DecoratorInstancerInterface& instancer_interface) override;
77+
78+
private:
79+
struct PropertyIds {
80+
PropertyId text, color, align_x, align_y;
81+
};
82+
PropertyIds ids;
83+
};
84+
85+
} // namespace Rml
86+
#endif

0 commit comments

Comments
 (0)