blob: e1b13434b553f12dd377311151ef9e21089992e7 [file] [log] [blame]
#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(), 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;
}