| #include "rive/text/text_style.hpp" | |
| #include "rive/text/text_style_axis.hpp" | |
| #include "rive/text/text_style_feature.hpp" | |
| #include "rive/renderer.hpp" | |
| #include "rive/shapes/paint/shape_paint.hpp" | |
| #include "rive/backboard.hpp" | |
| #include "rive/importers/backboard_importer.hpp" | |
| #include "rive/text/text.hpp" | |
| #include "rive/artboard.hpp" | |
| #include "rive/factory.hpp" | |
| using namespace rive; | |
| namespace rive | |
| { | |
| class TextVariationHelper : public Component | |
| { | |
| public: | |
| TextVariationHelper(TextStyle* style) : m_textStyle(style) {} | |
| TextStyle* style() const { return m_textStyle; } | |
| void buildDependencies() override | |
| { | |
| auto text = m_textStyle->parent(); | |
| text->artboard()->addDependent(this); | |
| addDependent(text); | |
| } | |
| void update(ComponentDirt value) override { m_textStyle->updateVariableFont(); } | |
| private: | |
| TextStyle* m_textStyle; | |
| }; | |
| } // namespace rive | |
| // satisfy unique_ptr | |
| TextStyle::TextStyle() {} | |
| void TextStyle::addVariation(TextStyleAxis* axis) { m_variations.push_back(axis); } | |
| void TextStyle::addFeature(TextStyleFeature* feature) { m_styleFeatures.push_back(feature); } | |
| void TextStyle::onDirty(ComponentDirt dirt) | |
| { | |
| if ((dirt & ComponentDirt::TextShape) == ComponentDirt::TextShape) | |
| { | |
| parent()->as<Text>()->markShapeDirty(); | |
| if (m_variationHelper != nullptr) | |
| { | |
| m_variationHelper->addDirt(ComponentDirt::TextShape); | |
| } | |
| } | |
| } | |
| StatusCode TextStyle::onAddedClean(CoreContext* context) | |
| { | |
| auto code = Super::onAddedClean(context); | |
| if (code != StatusCode::Ok) | |
| { | |
| return code; | |
| } | |
| // This ensures context propagates to variation helper too. | |
| if (!m_variations.empty()) | |
| { | |
| m_variationHelper = rivestd::make_unique<TextVariationHelper>(this); | |
| } | |
| if (m_variationHelper != nullptr) | |
| { | |
| if ((code = m_variationHelper->onAddedDirty(context)) != StatusCode::Ok) | |
| { | |
| return code; | |
| } | |
| if ((code = m_variationHelper->onAddedClean(context)) != StatusCode::Ok) | |
| { | |
| return code; | |
| } | |
| } | |
| return StatusCode::Ok; | |
| } | |
| const rcp<Font> TextStyle::font() const | |
| { | |
| if (m_variableFont != nullptr) | |
| { | |
| return m_variableFont; | |
| } | |
| auto asset = fontAsset(); | |
| return asset == nullptr ? nullptr : asset->font(); | |
| } | |
| void TextStyle::updateVariableFont() | |
| { | |
| auto asset = fontAsset(); | |
| rcp<Font> baseFont = asset == nullptr ? nullptr : asset->font(); | |
| if (baseFont == nullptr) | |
| { | |
| // Not ready yet. | |
| return; | |
| } | |
| if (!m_variations.empty() || !m_styleFeatures.empty()) | |
| { | |
| m_coords.clear(); | |
| for (TextStyleAxis* axis : m_variations) | |
| { | |
| m_coords.push_back({axis->tag(), axis->axisValue()}); | |
| } | |
| m_features.clear(); | |
| for (TextStyleFeature* styleFeature : m_styleFeatures) | |
| { | |
| m_features.push_back({styleFeature->tag(), styleFeature->featureValue()}); | |
| } | |
| m_variableFont = baseFont->withOptions(m_coords, m_features); | |
| } | |
| else | |
| { | |
| m_variableFont = nullptr; | |
| } | |
| } | |
| void TextStyle::buildDependencies() | |
| { | |
| if (m_variationHelper != nullptr) | |
| { | |
| m_variationHelper->buildDependencies(); | |
| } | |
| parent()->addDependent(this); | |
| Super::buildDependencies(); | |
| auto factory = getArtboard()->factory(); | |
| m_path = factory->makeEmptyRenderPath(); | |
| } | |
| void TextStyle::rewindPath() | |
| { | |
| m_path->rewind(); | |
| m_hasContents = false; | |
| m_opacityPaths.clear(); | |
| } | |
| bool TextStyle::addPath(const RawPath& rawPath, float opacity) | |
| { | |
| bool hadContents = m_hasContents; | |
| m_hasContents = true; | |
| if (opacity == 1.0f) | |
| { | |
| rawPath.addTo(m_path.get()); | |
| } | |
| else if (opacity > 0.0f) | |
| { | |
| auto itr = m_opacityPaths.find(opacity); | |
| RenderPath* renderPath = nullptr; | |
| if (itr != m_opacityPaths.end()) | |
| { | |
| renderPath = itr->second.get(); | |
| } | |
| else | |
| { | |
| auto factory = getArtboard()->factory(); | |
| auto erp = factory->makeEmptyRenderPath(); | |
| renderPath = erp.get(); | |
| m_opacityPaths[opacity] = std::move(erp); | |
| } | |
| rawPath.addTo(renderPath); | |
| } | |
| return !hadContents; | |
| } | |
| void TextStyle::draw(Renderer* renderer) | |
| { | |
| auto path = m_path.get(); | |
| for (auto shapePaint : m_ShapePaints) | |
| { | |
| if (!shapePaint->isVisible()) | |
| { | |
| continue; | |
| } | |
| shapePaint->draw(renderer, path); | |
| if (m_paintPool.size() < m_opacityPaths.size()) | |
| { | |
| m_paintPool.reserve(m_opacityPaths.size()); | |
| Factory* factory = artboard()->factory(); | |
| while (m_paintPool.size() < m_opacityPaths.size()) | |
| { | |
| m_paintPool.emplace_back(factory->makeRenderPaint()); | |
| } | |
| } | |
| uint32_t paintIndex = 0; | |
| for (auto itr = m_opacityPaths.begin(); itr != m_opacityPaths.end(); itr++) | |
| { | |
| RenderPaint* renderPaint = m_paintPool[paintIndex++].get(); | |
| shapePaint->applyTo(renderPaint, itr->first); | |
| shapePaint->draw(renderer, itr->second.get(), nullptr, renderPaint); | |
| } | |
| } | |
| } | |
| uint32_t TextStyle::assetId() { return this->fontAssetId(); } | |
| void TextStyle::setAsset(FileAsset* asset) | |
| { | |
| if (asset->is<FontAsset>()) | |
| { | |
| FileAssetReferencer::setAsset(asset); | |
| } | |
| } | |
| StatusCode TextStyle::import(ImportStack& importStack) | |
| { | |
| auto result = registerReferencer(importStack); | |
| if (result != StatusCode::Ok) | |
| { | |
| return result; | |
| } | |
| return Super::import(importStack); | |
| } | |
| void TextStyle::fontSizeChanged() { parent()->as<Text>()->markShapeDirty(); } | |
| void TextStyle::lineHeightChanged() { parent()->as<Text>()->markShapeDirty(); } | |
| void TextStyle::letterSpacingChanged() { parent()->as<Text>()->markShapeDirty(); } | |
| Core* TextStyle::clone() const | |
| { | |
| TextStyle* twin = TextStyleBase::clone()->as<TextStyle>(); | |
| if (m_fileAsset != nullptr) | |
| { | |
| twin->setAsset(m_fileAsset); | |
| } | |
| return twin; | |
| } |