|  | #include "rive/shapes/shape.hpp" | 
|  | #include "rive/shapes/clipping_shape.hpp" | 
|  | #include "rive/shapes/paint/blend_mode.hpp" | 
|  | #include "rive/shapes/paint/shape_paint.hpp" | 
|  | #include "rive/shapes/path_composer.hpp" | 
|  | #include <algorithm> | 
|  |  | 
|  | using namespace rive; | 
|  |  | 
|  | Shape::Shape() : m_PathComposer(this) {} | 
|  |  | 
|  | void Shape::addPath(Path* path) | 
|  | { | 
|  | // Make sure the path is not already in the shape. | 
|  | assert(std::find(m_Paths.begin(), m_Paths.end(), path) == m_Paths.end()); | 
|  | m_Paths.push_back(path); | 
|  | } | 
|  |  | 
|  | void Shape::update(ComponentDirt value) | 
|  | { | 
|  | Super::update(value); | 
|  |  | 
|  | if (hasDirt(value, ComponentDirt::RenderOpacity)) | 
|  | { | 
|  | for (auto shapePaint : m_ShapePaints) | 
|  | { | 
|  | shapePaint->renderOpacity(renderOpacity()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Shape::pathChanged() | 
|  | { | 
|  | m_PathComposer.addDirt(ComponentDirt::Path, true); | 
|  | invalidateStrokeEffects(); | 
|  | } | 
|  |  | 
|  | void Shape::draw(Renderer* renderer) | 
|  | { | 
|  | if (renderOpacity() == 0.0f) | 
|  | { | 
|  | return; | 
|  | } | 
|  | auto shouldRestore = clip(renderer); | 
|  |  | 
|  | for (auto shapePaint : m_ShapePaints) | 
|  | { | 
|  | if (!shapePaint->isVisible()) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | renderer->save(); | 
|  | bool paintsInLocal = | 
|  | (shapePaint->pathSpace() & PathSpace::Local) == PathSpace::Local; | 
|  | if (paintsInLocal) | 
|  | { | 
|  | const Mat2D& transform = worldTransform(); | 
|  | renderer->transform(transform); | 
|  | } | 
|  | shapePaint->draw(renderer, | 
|  | paintsInLocal ? m_PathComposer.localPath() | 
|  | : m_PathComposer.worldPath()); | 
|  | renderer->restore(); | 
|  | } | 
|  |  | 
|  | if (shouldRestore) | 
|  | { | 
|  | renderer->restore(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Shape::buildDependencies() | 
|  | { | 
|  | // Make sure to propagate the call to PathComposer as it's no longer part of | 
|  | // Core and owned only by the Shape. | 
|  | m_PathComposer.buildDependencies(); | 
|  |  | 
|  | Super::buildDependencies(); | 
|  |  | 
|  | // Set the blend mode on all the shape paints. If we ever animate this | 
|  | // property, we'll need to update it in the update cycle/mark dirty when the | 
|  | // blend mode changes. | 
|  | for (auto paint : m_ShapePaints) | 
|  | { | 
|  | paint->blendMode(blendMode()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Shape::addDefaultPathSpace(PathSpace space) | 
|  | { | 
|  | m_DefaultPathSpace |= space; | 
|  | } | 
|  |  | 
|  | StatusCode Shape::onAddedDirty(CoreContext* context) | 
|  | { | 
|  | auto code = Super::onAddedDirty(context); | 
|  | if (code != StatusCode::Ok) | 
|  | { | 
|  | return code; | 
|  | } | 
|  | // This ensures context propagates to path composer too. | 
|  | return m_PathComposer.onAddedDirty(context); | 
|  | } |