| #ifndef _RIVE_SHAPE_PAINT_HPP_ | |
| #define _RIVE_SHAPE_PAINT_HPP_ | |
| #include "rive/generated/shapes/paint/shape_paint_base.hpp" | |
| #include "rive/shapes/paint/effects_container.hpp" | |
| #include "rive/renderer.hpp" | |
| #include "rive/shapes/paint/blend_mode.hpp" | |
| #include "rive/shapes/paint/shape_paint_mutator.hpp" | |
| #include "rive/shapes/path_flags.hpp" | |
| #include "rive/shapes/shape_paint_path.hpp" | |
| #include "rive/shapes/paint/stroke_effect.hpp" | |
| #include "rive/shapes/paint/artboard_paint_source.hpp" | |
| #include "rive/math/raw_path.hpp" | |
| namespace rive | |
| { | |
| class RenderPaint; | |
| class ShapePaintMutator; | |
| class Feather; | |
| class ShapePaintContainer; | |
| class TransformComponent; | |
| class ShapePaint : public ShapePaintBase, | |
| public EffectsContainer, | |
| public PathProvider | |
| { | |
| protected: | |
| rcp<RenderPaint> m_RenderPaint; | |
| ShapePaintMutator* m_PaintMutator = nullptr; | |
| // update() skipped measuring the effects because this paint was fully | |
| // transparent. Showing the paint again owes them a run. | |
| bool m_effectsDeferred = false; | |
| public: | |
| StatusCode onAddedClean(CoreContext* context) override; | |
| void invalidateEffects(StrokeEffect* effect) override; | |
| void invalidateEffects() override; | |
| virtual void invalidateRendering(); | |
| float renderOpacity() const { return m_PaintMutator->renderOpacity(); } | |
| void renderOpacity(float value); | |
| /// Syncs this paint's blend mode onto its RenderPaint. A ShapePaint whose | |
| /// blendModeValue is 127 inherits both the mode and the additive amount | |
| /// from its parent drawable, hence both parameters. | |
| void blendMode(BlendMode parentValue, uint8_t parentAdditiveAmount); | |
| /// 127 in the mode byte means "take the parent drawable's blend mode". | |
| bool inheritsBlendMode() const { return blendModeValue() == 127; } | |
| BlendMode blendMode() const { return (BlendMode)blendModeValue(); } | |
| void additiveAmountChanged() override; | |
| void addStrokeEffect(StrokeEffect* effect) override; | |
| /// Creates a RenderPaint object for the provided ShapePaintMutator*. | |
| /// This should be called only once as the ShapePaint manages the | |
| /// lifecycle of the RenderPaint. | |
| virtual RenderPaint* initRenderPaint(ShapePaintMutator* mutator); | |
| virtual PathFlags pathFlags() const = 0; | |
| bool isFlagged(PathFlags flags) const | |
| { | |
| return (int)(pathFlags() & flags) != 0x00; | |
| } | |
| virtual void draw( | |
| Renderer* renderer, | |
| ShapePaintPath* shapePaintPath, | |
| const Mat2D& transform, | |
| bool usePathFillRule = false, | |
| RenderPaint* overridePaint = nullptr, | |
| bool needsSaveOperation = true); | |
| RenderPaint* renderPaint() { return m_RenderPaint.get(); } | |
| /// Get the component that represents the ShapePaintMutator for this | |
| /// ShapePaint. It'll be one of SolidColor, LinearGradient, or | |
| /// RadialGradient. | |
| Component* paint() const { return m_PaintMutator->component(); } | |
| bool isTranslucent() const | |
| { | |
| return !this->isVisible() || m_PaintMutator->isTranslucent(); | |
| } | |
| bool shouldDraw() const | |
| { | |
| return this->isVisible() && m_PaintMutator->isVisible(); | |
| } | |
| /// Apply this ShapePaint to an external RenderPaint and optionally modulate | |
| /// the opacity by opacityModifer. | |
| virtual void applyTo(RenderPaint* renderPaint, float opacityModifier) = 0; | |
| void feather(Feather* feather); | |
| Feather* feather() const; | |
| #ifdef WITH_RIVE_EDITOR | |
| /// Set the feather pointer in editor mode (idempotent). | |
| void setFeatherForEditor(Feather* f) { m_feather = f; } | |
| /// Clear m_feather only if it currently points at `expected`. | |
| void clearFeatherIfForEditor(Feather* expected) | |
| { | |
| if (m_feather == expected) | |
| { | |
| m_feather = nullptr; | |
| } | |
| } | |
| // Edit-time reparent/remove dispatch — registers this paint into | |
| // the new parent's `m_ShapePaints` and removes from the old | |
| // parent's. Without this, removing a Fill/Stroke leaves a stale | |
| // pointer in the host Shape's m_ShapePaints, which `buildDeps` | |
| // and `pathChanged` iterate and crash on. Body in | |
| // `component_parent_editor.cpp`. | |
| void editorParentChanged(ContainerComponent* from, ContainerComponent* to) | |
| override; | |
| #endif | |
| virtual ShapePaintPath* pickPath(ShapePaintContainer* shape) const = 0; | |
| void update(ComponentDirt value) override; | |
| virtual ShapePaintType paintType() const = 0; | |
| TransformComponent* parentTransformComponent() const; | |
| private: | |
| /// Install (or clear) the modulating image contributed by the optional | |
| /// PaintImage child onto m_RenderPaint, fit to [path]'s bounds. The bounds | |
| /// walk every point, so they are only measured when that child exists. | |
| /// [target] receives the renderer the path has to be drawn through, which | |
| /// is [renderer] unless an artboard source had to reroute it. | |
| void applyModulatedImage( | |
| Renderer* renderer, | |
| const ShapePaintPath* path, | |
| ModulatedDrawTarget* target); | |
| Feather* m_feather = nullptr; | |
| /// Whether this paint has a PaintImage child, and whether it is a Fill, | |
| /// both found once all children are known (onAddedClean): draw() needs | |
| /// them on every call, and at runtime neither changes after import. They | |
| /// sit in padding, so they cost no memory, and only a paint that has the | |
| /// child looks it up. The editor can add or remove the child, so it checks | |
| /// both on every draw instead. | |
| bool m_hasPaintImage = false; | |
| bool m_isFill = false; | |
| /// Whether the last draw installed a modulating image on m_RenderPaint. The | |
| /// paint persists across draws, so we track this to clear it once the | |
| /// PaintImage child (or its asset) goes away. | |
| bool m_hasModulatedImage = false; | |
| }; | |
| } // namespace rive | |
| #endif |