blob: be982059598593e766a62d8eda49d36f9d74d839 [file]
#ifndef _RIVE_PAINT_IMAGE_HPP_
#define _RIVE_PAINT_IMAGE_HPP_
#include "rive/generated/shapes/paint/paint_image_base.hpp"
#include "rive/shapes/paint/image_sampler.hpp"
#include "rive/advancing_component.hpp"
#include "rive/artboard_host.hpp"
#include "rive/artboard_referencer.hpp"
#include "rive/assets/file_asset_referencer.hpp"
#include "rive/math/aabb.hpp"
#include "rive/math/mat2d.hpp"
#include <memory>
namespace rive
{
class ArtboardPaintSource;
class RenderPaint;
class Renderer;
class ImageAsset;
struct ModulatedDrawTarget;
/// Optional child of a ShapePaint that modulates (multiplies) the parent
/// paint's color/gradient with an image when painting the path. Discovered by
/// the parent via ContainerComponent::firstChild<PaintImage>() (no back-pointer
/// is stored on the ShapePaint).
///
/// The image is either an image asset or an artboard. An artboard source is
/// instanced per paint instance, advanced on its default state machine, and
/// rasterized into an offscreen canvas whenever it changes; it takes
/// precedence over the asset when it resolves. The source binds to the data
/// context of the artboard the paint is in, which reaches it through this
/// paint as an ArtboardHost.
class PaintImage : public PaintImageBase,
public FileAssetReferencer,
public ArtboardReferencer,
public ArtboardHost,
public AdvancingComponent
{
public:
// Out of line so the unique_ptr<ArtboardPaintSource> is only destroyed
// where its type is complete.
PaintImage();
~PaintImage() override;
StatusCode import(ImportStack& importStack) override;
bool validate(CoreContext* context) override;
StatusCode onAddedClean(CoreContext* context) override;
void buildDependencies() override;
void update(ComponentDirt value) override;
Core* clone() const override;
// FileAssetReferencer: the image used to modulate the parent paint.
void setAsset(rcp<FileAsset>) override;
uint32_t assetId() override;
void assetUpdated() override;
ImageAsset* imageAsset() const;
ImageSampler imageSampler() const;
// ArtboardReferencer: the artboard used to modulate the parent paint. The
// referenced artboard is always the file level source; each instance of
// the host owns its own instance of it.
void updateArtboard(ViewModelInstanceArtboard*) override {}
int referencedArtboardId() override { return (int)artboardId(); }
// So the importer's cycle cut covers paint sources as well as nested
// artboards.
Artboard* nestingArtboard() override { return artboard(); }
Artboard* sourceArtboard() const { return m_referencedArtboard; }
// AdvancingComponent: plays the artboard source. An image source never
// advances, so it never keeps a list row from going quiet.
bool advanceComponent(
float elapsedSeconds,
AdvanceFlags flags = AdvanceFlags::Animate |
AdvanceFlags::NewFrame) override;
QuietState quietState() override
{
return m_source != nullptr ? QuietState::never : QuietState::quiet;
}
// ArtboardHost: the artboard's data context reaches the source through
// here, passed through unchanged as a NestedArtboard with no bind path
// does. Only a paint with an artboard source registers as a host.
size_t artboardCount() override { return m_source != nullptr ? 1 : 0; }
ArtboardInstance* artboardInstance(int index = 0) override;
void internalDataContext(rcp<DataContext> dataContext) override;
void bindViewModelInstance(
rcp<ViewModelInstance> viewModelInstance,
rcp<DataContext> parent) override;
void relinkDataContext(rcp<ViewModelInstance> viewModelInstance) override;
void clearDataContext() override;
void unbind() override;
void updateDataBinds() override;
Artboard* parentArtboard() override { return artboard(); }
// Nothing hit tests or lays out through a paint.
bool hitTestHost(const Vec2D&, bool, ArtboardInstance*) override
{
return false;
}
Vec2D hostTransformPoint(const Vec2D& point, ArtboardInstance*) override
{
return point;
}
Mat2D worldTransformForArtboard(ArtboardInstance*) override
{
return Mat2D();
}
void markHostTransformDirty() override {}
void file(File* value) override { m_file = value; }
File* file() const override { return m_file; }
// hostComponent() stays null on purpose: focus, semantics and parent
// traversal cross into a host through it, and the source is a picture of
// an artboard, not part of the hierarchy it is painted in.
int type() const override { return coreType(); }
/// Modulate [paint] with this image, auto-fit to [bounds] (the shape's
/// local bounds) and adjusted by the image transform (scale/offset/rotation
/// about the bounds center). Returns false (leaving [paint] untouched) when
/// no image is resolved -- the caller owns clearing any stale image, as it
/// also has to handle this component being removed entirely.
///
/// [renderer] is what the path is about to be drawn with: an artboard
/// source sizes its raster from its transform. [target] receives the
/// renderer the path has to be drawn through instead, if any.
bool applyTo(
Renderer* renderer,
RenderPaint* paint,
const AABB& bounds,
ModulatedDrawTarget* target);
/// Maps the image's normalized 0..1 space onto [bounds]. The base fits the
/// image across the bounds once ("fill") or at [originalWidth] x
/// [originalHeight] local units ("original"); the user transform
/// (scale/offset/rotation about the bounds center) modifies it.
Mat2D textureTransform(
const AABB& bounds,
float originalWidth,
float originalHeight) const;
#ifdef TESTING
ArtboardPaintSource* artboardSource() const { return m_source.get(); }
#endif
private:
void invalidatePaint();
std::unique_ptr<ArtboardPaintSource> m_source;
File* m_file = nullptr;
};
} // namespace rive
#endif