| #include "shapes/paint/linear_gradient.hpp" | |
| #include "math/vec2d.hpp" | |
| #include "node.hpp" | |
| #include "renderer.hpp" | |
| #include "shapes/paint/color.hpp" | |
| #include "shapes/paint/gradient_stop.hpp" | |
| #include "shapes/shape_paint_container.hpp" | |
| #include "shapes/paint/shape_paint.hpp" | |
| #include <algorithm> | |
| using namespace rive; | |
| StatusCode LinearGradient::onAddedDirty(CoreContext* context) | |
| { | |
| StatusCode code = Super::onAddedDirty(context); | |
| if (code != StatusCode::Ok) | |
| { | |
| return code; | |
| } | |
| if (!initPaintMutator(this)) | |
| { | |
| return StatusCode::MissingObject; | |
| } | |
| return StatusCode::Ok; | |
| } | |
| void LinearGradient::buildDependencies() | |
| { | |
| auto p = parent(); | |
| if (p != nullptr && p->parent() != nullptr) | |
| { | |
| auto parentsParent = p->parent(); | |
| // Parent's parent must be a shape paint container. | |
| assert(ShapePaintContainer::from(parentsParent) != nullptr); | |
| // TODO: see if artboard should inherit from some TransformComponent | |
| // that can return a world transform. We store the container just for | |
| // doing the transform to world in update. If it's the artboard, then | |
| // we're already in world so no need to transform. | |
| m_ShapePaintContainer = | |
| parentsParent->is<Node>() ? parentsParent->as<Node>() : nullptr; | |
| parentsParent->addDependent(this); | |
| } | |
| } | |
| void LinearGradient::addStop(GradientStop* stop) { m_Stops.push_back(stop); } | |
| static bool stopsComparer(GradientStop* a, GradientStop* b) | |
| { | |
| return a->position() < b->position(); | |
| } | |
| void LinearGradient::update(ComponentDirt value) | |
| { | |
| // Do the stops need to be re-ordered? | |
| bool stopsChanged = hasDirt(value, ComponentDirt::Stops); | |
| if (stopsChanged) | |
| { | |
| std::sort(m_Stops.begin(), m_Stops.end(), stopsComparer); | |
| } | |
| bool worldTransformed = hasDirt(value, ComponentDirt::WorldTransform); | |
| bool paintsInWorldSpace = | |
| parent()->as<ShapePaint>()->pathSpace() == PathSpace::World; | |
| // We rebuild the gradient if the gradient is dirty or we paint in world | |
| // space and the world space transform has changed, or the local transform | |
| // has changed. Local transform changes when a stop moves in local space. | |
| bool rebuildGradient = | |
| hasDirt(value, | |
| ComponentDirt::Paint | ComponentDirt::RenderOpacity | | |
| ComponentDirt::Transform) || | |
| (paintsInWorldSpace && worldTransformed); | |
| if (rebuildGradient) | |
| { | |
| auto paint = renderPaint(); | |
| Vec2D start(startX(), startY()); | |
| Vec2D end(endX(), endY()); | |
| // Check if we need to update the world space gradient (if there's no | |
| // shape container, presumably it's the artboard and we're already in | |
| // world). | |
| if (paintsInWorldSpace && m_ShapePaintContainer != nullptr) | |
| { | |
| // Get the start and end of the gradient in world coordinates (world | |
| // transform of the shape). | |
| const Mat2D& world = m_ShapePaintContainer->worldTransform(); | |
| Vec2D worldStart; | |
| Vec2D::transform(worldStart, start, world); | |
| Vec2D worldEnd; | |
| Vec2D::transform(worldEnd, end, world); | |
| makeGradient(worldStart, worldEnd); | |
| } | |
| else | |
| { | |
| makeGradient(start, end); | |
| } | |
| // build up the color and positions lists | |
| double ro = opacity() * renderOpacity(); | |
| for (auto stop : m_Stops) | |
| { | |
| paint->addStop( | |
| colorModulateOpacity((unsigned int)stop->colorValue(), ro), | |
| stop->position()); | |
| } | |
| paint->completeGradient(); | |
| } | |
| } | |
| void LinearGradient::makeGradient(const Vec2D& start, const Vec2D& end) | |
| { | |
| renderPaint()->linearGradient(start[0], start[1], end[0], end[1]); | |
| } | |
| void LinearGradient::markGradientDirty() { addDirt(ComponentDirt::Paint); } | |
| void LinearGradient::markStopsDirty() | |
| { | |
| addDirt(ComponentDirt::Paint | ComponentDirt::Stops); | |
| } | |
| void LinearGradient::renderOpacityChanged() { markGradientDirty(); } | |
| void LinearGradient::startXChanged() { addDirt(ComponentDirt::Transform); } | |
| void LinearGradient::startYChanged() { addDirt(ComponentDirt::Transform); } | |
| void LinearGradient::endXChanged() { addDirt(ComponentDirt::Transform); } | |
| void LinearGradient::endYChanged() { addDirt(ComponentDirt::Transform); } | |
| void LinearGradient::opacityChanged() { markGradientDirty(); } |