blob: d942d5cba6753e0b5bbb75d08dd4511865d7a6f2 [file]
#include "rive/text/text_style_paint.hpp"
#include "rive/text/text.hpp"
#include "rive/text/text_variation_helper.hpp"
#include "rive/shapes/paint/shape_paint.hpp"
#include "rive/shapes/paint/fill.hpp"
#include "rive/shapes/paint/solid_color.hpp"
#include "rive/shapes/paint/feather.hpp"
#include "rive/artboard.hpp"
#include "rive/factory.hpp"
using namespace rive;
TextStylePaint::TextStylePaint() {}
void TextStylePaint::rewindPath() { m_geometry.rewind(); }
bool TextStylePaint::addPath(
const RawPath& rawPath,
float opacity,
const Mat2D* transform)
{
return m_geometry.addPath(rawPath, opacity, transform);
}
void TextStylePaint::draw(Renderer* renderer, const Mat2D& worldTransform)
{
drawGeometry(renderer, worldTransform, m_geometry, 1.0f);
}
void TextStylePaint::drawGeometry(
Renderer* renderer,
const Mat2D& worldTransform,
TextStyleGeometry& geometry,
float opacityModifier)
{
// A paint shared by several Texts (opacityModifier carries the drawing
// Text's opacity) never has its own render paints touched: every bucket,
// the opaque one included, draws through the geometry's pooled paints.
const bool owned = opacityModifier == 1.0f;
auto& opacityPaths = geometry.opacityPaths();
for (auto shapePaint : m_ShapePaints)
{
if (!shapePaint->shouldDraw())
{
continue;
}
// A global text style's paint lives under the style Artboard, not a
// Text; keep its shape paint's own (default) blend mode in that case.
if (owned && parent()->is<Text>())
{
auto text = parent()->as<Text>();
shapePaint->blendMode(text->blendMode(), text->additiveAmount());
}
auto drawPooled =
[&](ShapePaintPath* path, float opacity, uint32_t paintIndex) {
RenderPaint* renderPaint = geometry.pooledPaint(paintIndex);
shapePaint->applyTo(renderPaint, opacity * opacityModifier);
// Pooled paints are shared across shape paints; always set
// feather and blend mode so one paint's settings don't leak into
// the others. Pooled draws take the shape paint's own mode (a
// global style's paint has no Text to inherit from).
auto feather = shapePaint->feather();
renderPaint->feather(feather != nullptr ? feather->strength() : 0.0f);
if (!owned)
{
BlendMode mode = shapePaint->inheritsBlendMode()
? BlendMode::srcOver
: shapePaint->blendMode();
renderPaint->blendMode(mode);
renderPaint->additiveness(
additivenessFor(mode, shapePaint->additiveAmount()));
}
shapePaint->draw(renderer, path, worldTransform, true, renderPaint);
};
// An effect (dash, trim...) is built from every glyph and replaces
// whichever bucket's path ShapePaint::draw is handed, so drawing it per
// bucket would stack the whole outline once per opacity. Draw it once,
// at the highest opacity any glyph has. The effect's output can't be
// traced back to glyphs, so fainter glyphs it keeps draw at that opacity
// too.
if (shapePaint->lastEffectPath(shapePaint) != nullptr)
{
if (opacityPaths.empty())
{
continue;
}
float opacity = opacityPaths.rbegin()->first;
if (owned && opacity == 1.0f)
{
shapePaint->draw(renderer, geometry.path(), worldTransform, true);
}
else
{
geometry.reservePaints(artboard()->factory());
drawPooled(geometry.path(), opacity, 0);
}
continue;
}
// For blend modes to work, opaque paths render first
auto itr = opacityPaths.find(1.0f);
if (owned && itr != opacityPaths.end())
{
ShapePaintPath& path = itr->second;
shapePaint->draw(renderer, &path, worldTransform, true);
}
geometry.reservePaints(artboard()->factory());
uint32_t paintIndex = 0;
for (itr = opacityPaths.begin(); itr != opacityPaths.end(); itr++)
{
// Don't render opaque paths twice
if (owned && itr->first == 1.0f)
{
continue;
}
drawPooled(&itr->second, itr->first, paintIndex++);
}
}
}
ColorInt TextStylePaint::foregroundColor() const
{
for (auto shapePaint : m_ShapePaints)
{
if (shapePaint->is<Fill>() && shapePaint->paint()->is<SolidColor>())
{
return (ColorInt)shapePaint->paint()->as<SolidColor>()->colorValue();
}
}
return 0xFF000000;
}
const Mat2D& TextStylePaint::shapeWorldTransform() const
{
// Global text style paints are parented to the style Artboard; fall back to
// the artboard's transform (only used for feather inner-path rebuilds).
if (parent()->is<Text>())
{
return parent()->as<Text>()->shapeWorldTransform();
}
return artboard()->shapeWorldTransform();
}
Component* TextStylePaint::pathBuilder() { return parent(); }
Core* TextStylePaint::clone() const
{
TextStylePaint* twin = TextStylePaintBase::clone()->as<TextStylePaint>();
cloneFontInto(twin);
return twin;
}