| #include "rive/shapes/clipping_shape.hpp" | |
| #include "rive/artboard.hpp" | |
| #include "rive/core_context.hpp" | |
| #include "rive/factory.hpp" | |
| #include "rive/node.hpp" | |
| #include "rive/renderer.hpp" | |
| #include "rive/shapes/path_composer.hpp" | |
| #include "rive/shapes/shape.hpp" | |
| using namespace rive; | |
| StatusCode ClippingShape::onAddedClean(CoreContext* context) | |
| { | |
| auto clippingHolder = parent(); | |
| auto artboard = static_cast<Artboard*>(context); | |
| for (auto core : artboard->objects()) | |
| { | |
| if (core == nullptr) | |
| { | |
| continue; | |
| } | |
| // Iterate artboard to find drawables that are parented to this clipping | |
| // shape, they need to know they'll be clipped by this shape. | |
| if (core->is<Drawable>()) | |
| { | |
| auto drawable = core->as<Drawable>(); | |
| for (ContainerComponent* component = drawable; component != nullptr; | |
| component = component->parent()) | |
| { | |
| if (component == clippingHolder) | |
| { | |
| drawable->addClippingShape(this); | |
| break; | |
| } | |
| } | |
| } | |
| // Iterate artboard to find shapes that are parented to the source, | |
| // their paths will need to be RenderPaths in order to be used for | |
| // clipping operations. | |
| if (core->is<Shape>() && core != clippingHolder) | |
| { | |
| auto component = core->as<ContainerComponent>(); | |
| while (component != nullptr) | |
| { | |
| if (component == m_Source) | |
| { | |
| auto shape = core->as<Shape>(); | |
| shape->addDefaultPathSpace(PathSpace::World | PathSpace::Clipping); | |
| m_Shapes.push_back(shape); | |
| break; | |
| } | |
| component = component->parent(); | |
| } | |
| } | |
| } | |
| // We only need a render path if we have more than 1 clip path as we join them. | |
| if (m_Shapes.size() > 1) | |
| { | |
| m_RenderPath = artboard->factory()->makeEmptyRenderPath(); | |
| } | |
| return StatusCode::Ok; | |
| } | |
| StatusCode ClippingShape::onAddedDirty(CoreContext* context) | |
| { | |
| StatusCode code = Super::onAddedDirty(context); | |
| if (code != StatusCode::Ok) | |
| { | |
| return code; | |
| } | |
| auto coreObject = context->resolve(sourceId()); | |
| if (coreObject == nullptr || !coreObject->is<Node>()) | |
| { | |
| return StatusCode::MissingObject; | |
| } | |
| m_Source = static_cast<Node*>(coreObject); | |
| return StatusCode::Ok; | |
| } | |
| void ClippingShape::buildDependencies() | |
| { | |
| for (auto shape : m_Shapes) | |
| { | |
| shape->pathComposer()->addDependent(this); | |
| } | |
| } | |
| static Mat2D identity; | |
| void ClippingShape::update(ComponentDirt value) | |
| { | |
| if (hasDirt(value, ComponentDirt::Path | ComponentDirt::WorldTransform)) | |
| { | |
| if (m_RenderPath) | |
| { | |
| m_RenderPath->reset(); | |
| m_RenderPath->fillRule((FillRule)fillRule()); | |
| m_ClipRenderPath = nullptr; | |
| for (auto shape : m_Shapes) | |
| { | |
| if (!shape->isHidden()) | |
| { | |
| m_RenderPath->addPath(shape->pathComposer()->worldPath(), identity); | |
| m_ClipRenderPath = m_RenderPath.get(); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| auto first = m_Shapes.front(); | |
| if (first->isHidden()) | |
| { | |
| m_ClipRenderPath = nullptr; | |
| } | |
| else | |
| { | |
| m_ClipRenderPath = first->pathComposer()->worldPath()->renderPath(); | |
| } | |
| } | |
| } | |
| } |