mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
Text modifiers!
Adds support for text modifiers, which are a set a of operations that can run on a per character basis to transform, reshape, and fade resulting glyphs. Adds: - Modifier groups which encompass a set of modifications done to a range of the text. - Range selectors which help define the range the modifier applies to (multiple range selectors can be included to build up the final selection of a modifier group). - Variation modifiers which allow animating variable fonts per character. - Origin, Translation, Rotation, and Scale modification. - Opacity modifiers. Diffs= 9695de6e3 Text modifiers! (#5288)
This commit is contained in:
@@ -1 +1 @@
|
||||
a9f8a1c5d351a72b4537a6b8a27962d130f0a97c
|
||||
9695de6e34a963dd4fa4dc73b72e366c7a68ff04
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
|
||||
Axis getAxis(uint16_t index) const override;
|
||||
uint16_t getAxisCount() const override;
|
||||
float getAxisValue(uint32_t axisTag) const override;
|
||||
std::vector<Coord> getCoords() const override;
|
||||
rive::rcp<rive::Font> makeAtCoords(rive::Span<const Coord>) const override;
|
||||
rive::RawPath getPath(rive::GlyphID) const override;
|
||||
|
||||
@@ -132,6 +132,8 @@ public:
|
||||
//
|
||||
virtual std::vector<Coord> getCoords() const = 0;
|
||||
|
||||
virtual float getAxisValue(uint32_t axisTag) const = 0;
|
||||
|
||||
virtual rcp<Font> makeAtCoords(Span<const Coord>) const = 0;
|
||||
|
||||
rcp<Font> makeAtCoord(Coord c) { return this->makeAtCoords(Span<const Coord>(&c, 1)); }
|
||||
|
||||
@@ -153,6 +153,39 @@ uint16_t HBFont::getAxisCount() const
|
||||
return (uint16_t)hb_ot_var_get_axis_count(face);
|
||||
}
|
||||
|
||||
float HBFont::getAxisValue(uint32_t axisTag) const
|
||||
{
|
||||
auto face = hb_font_get_face(m_Font);
|
||||
uint32_t length;
|
||||
|
||||
// Check if we have a sepecified value.
|
||||
const float* values = hb_font_get_var_coords_design(m_Font, &length);
|
||||
for (uint32_t i = 0; i < length; ++i)
|
||||
{
|
||||
hb_ot_var_axis_info_t info;
|
||||
uint32_t n = 1;
|
||||
hb_ot_var_get_axis_infos(face, i, &n, &info);
|
||||
if (info.tag == axisTag)
|
||||
{
|
||||
return values[i];
|
||||
}
|
||||
}
|
||||
|
||||
// No value specified, we're using a default.
|
||||
uint32_t axisCount = hb_ot_var_get_axis_count(face);
|
||||
for (uint32_t i = 0; i < axisCount; ++i)
|
||||
{
|
||||
hb_ot_var_axis_info_t info;
|
||||
uint32_t n = 1;
|
||||
hb_ot_var_get_axis_infos(face, i, &n, &info);
|
||||
if (info.tag == axisTag)
|
||||
{
|
||||
return info.default_value;
|
||||
}
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
std::vector<rive::Font::Coord> HBFont::getCoords() const
|
||||
{
|
||||
auto axes = this->getAxes();
|
||||
@@ -220,7 +253,6 @@ static rive::GlyphRun shape_run(const rive::Unichar text[],
|
||||
hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
|
||||
|
||||
// todo: check for missing glyphs, and perform font-substitution
|
||||
|
||||
rive::GlyphRun gr(glyph_count);
|
||||
gr.font = tr.font;
|
||||
gr.size = tr.size;
|
||||
@@ -290,7 +322,11 @@ static void perform_fallback(rive::rcp<rive::Font> fallbackFont,
|
||||
orig.styleId,
|
||||
orig.dir,
|
||||
};
|
||||
gruns.add(shape_run(&text[textStart], tr, textStart));
|
||||
auto gr = shape_run(&text[textStart], tr, textStart);
|
||||
if (gr.glyphs.size() > 0)
|
||||
{
|
||||
gruns.add(std::move(gr));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -424,7 +460,10 @@ rive::SimpleArray<rive::Paragraph> HBFont::onShapeText(rive::Span<const rive::Un
|
||||
auto iter = std::find(gr.glyphs.begin(), end, 0);
|
||||
if (!gFallbackProc || iter == end)
|
||||
{
|
||||
gruns.add(std::move(gr));
|
||||
if (gr.glyphs.size() > 0)
|
||||
{
|
||||
gruns.add(std::move(gr));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -437,7 +476,7 @@ rive::SimpleArray<rive::Paragraph> HBFont::onShapeText(rive::Span<const rive::Un
|
||||
{
|
||||
perform_fallback(fallback, gruns, text.data(), gr, tr);
|
||||
}
|
||||
else
|
||||
else if (gr.glyphs.size() > 0)
|
||||
{
|
||||
gruns.add(std::move(gr)); // oh well, just keep the missing glyphs
|
||||
}
|
||||
|
||||
@@ -75,3 +75,36 @@ TEST_CASE("fallback glyphs are found", "[text]")
|
||||
fallbackFonts.clear();
|
||||
HBFont::gFallbackProc = nullptr;
|
||||
}
|
||||
|
||||
TEST_CASE("variable axis values can be read", "[text]")
|
||||
{
|
||||
REQUIRE(fallbackFonts.empty());
|
||||
auto font = loadFont("../../test/assets/RobotoFlex.ttf");
|
||||
REQUIRE(font != nullptr);
|
||||
|
||||
std::vector<rive::Font::Axis> axes = font->getAxes();
|
||||
|
||||
bool hasWeight = false;
|
||||
for (rive::Font::Axis axis : axes)
|
||||
{
|
||||
if (axis.tag == 2003265652)
|
||||
{
|
||||
REQUIRE(axis.def == 400.0f);
|
||||
hasWeight = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(hasWeight);
|
||||
|
||||
float value = font->getAxisValue(2003265652);
|
||||
REQUIRE(value == 400.0f);
|
||||
|
||||
rive::Font::Coord coord = {2003265652, 800.0f};
|
||||
rive::rcp<rive::Font> vfont = font->makeAtCoords(rive::Span<HBFont::Coord>(&coord, 1));
|
||||
REQUIRE(vfont->getAxisValue(2003265652) == 800.0f);
|
||||
|
||||
rive::Font::Coord coord2 = {2003265652, 822.0f};
|
||||
rive::rcp<rive::Font> vfont2 = vfont->makeAtCoords(rive::Span<HBFont::Coord>(&coord2, 1));
|
||||
REQUIRE(vfont2->getAxisValue(2003265652) == 822.0f);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,24 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
TEST_CASE("transform order is as expected", "[transform]")
|
||||
{
|
||||
auto translation = rive::Mat2D::fromTranslate(10.0f, 20.0f);
|
||||
auto rotation = rive::Mat2D::fromRotation(3.14f / 2.0f);
|
||||
auto scale = rive::Mat2D::fromScale(2.0f, 3.0f);
|
||||
|
||||
auto xform = translation * rotation * scale;
|
||||
auto xform2 = rive::Mat2D::fromRotation(3.14f / 2.0f);
|
||||
xform2[0] *= 2.0f;
|
||||
xform2[1] *= 2.0f;
|
||||
xform2[2] *= 3.0f;
|
||||
xform2[3] *= 3.0f;
|
||||
xform2[4] = 10.0f;
|
||||
xform2[5] = 20.0f;
|
||||
|
||||
REQUIRE(xform2 == xform);
|
||||
}
|
||||
|
||||
TEST_CASE("file can be read", "[file]")
|
||||
{
|
||||
RenderObjectLeakChecker checker;
|
||||
|
||||
Reference in New Issue
Block a user