Starting to abstract renderer for image assets.
diff --git a/README.md b/README.md index 12fa22a..182848f 100644 --- a/README.md +++ b/README.md
@@ -35,6 +35,9 @@ There's a VSCode command provided to ```run tests``` from the Tasks: Run Task command palette. +## Code Formatting +rive-cpp uses clang-format, you can install it with brew on MacOS: ```brew install clang-format```. + ## Memory Checks Note that if you're on MacOS you'll want to install valgrind, which is somewhat complicated these days. This is the easiest solution (please PR a better one when it becomes available). ```
diff --git a/include/rive/assets/file_asset.hpp b/include/rive/assets/file_asset.hpp index 50a84b5..5826e3a 100644 --- a/include/rive/assets/file_asset.hpp +++ b/include/rive/assets/file_asset.hpp
@@ -7,6 +7,8 @@ class FileAsset : public FileAssetBase { public: + virtual void decode(const uint8_t* bytes) = 0; + StatusCode import(ImportStack& importStack) override; }; } // namespace rive
diff --git a/include/rive/assets/file_asset_referencer.hpp b/include/rive/assets/file_asset_referencer.hpp new file mode 100644 index 0000000..7ba5976 --- /dev/null +++ b/include/rive/assets/file_asset_referencer.hpp
@@ -0,0 +1,14 @@ +#ifndef _RIVE_FILE_ASSET_REFERENCER_HPP_ +#define _RIVE_FILE_ASSET_REFERENCER_HPP_ + +namespace rive +{ + class FileAsset; + class FileAssetReferencer + { + public: + virtual void assets(const std::vector<FileAsset*>& assets) = 0; + }; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/assets/image_asset.hpp b/include/rive/assets/image_asset.hpp index c3fed2d..152ff58 100644 --- a/include/rive/assets/image_asset.hpp +++ b/include/rive/assets/image_asset.hpp
@@ -4,9 +4,18 @@ #include <stdio.h> namespace rive { + class RenderImage; class ImageAsset : public ImageAssetBase { + private: + RenderImage* m_RenderImage; + public: + ImageAsset(); + ~ImageAsset(); + + void decode(const uint8_t* bytes) override; + RenderImage* renderImage() const { return m_RenderImage; } }; } // namespace rive
diff --git a/include/rive/drawable.hpp b/include/rive/drawable.hpp index 4bd3568..fc1727f 100644 --- a/include/rive/drawable.hpp +++ b/include/rive/drawable.hpp
@@ -23,6 +23,7 @@ Drawable* next = nullptr; public: + BlendMode blendMode() const { return (BlendMode)blendModeValue(); } bool clip(Renderer* renderer) const; virtual void draw(Renderer* renderer) = 0; void addClippingShape(ClippingShape* shape);
diff --git a/include/rive/importers/backboard_importer.hpp b/include/rive/importers/backboard_importer.hpp index 6d7ba8d..be97d34 100644 --- a/include/rive/importers/backboard_importer.hpp +++ b/include/rive/importers/backboard_importer.hpp
@@ -10,12 +10,16 @@ class Artboard; class NestedArtboard; class Backboard; + class FileAsset; + class FileAssetReferencer; class BackboardImporter : public ImportStackObject { private: Backboard* m_Backboard; std::unordered_map<int, Artboard*> m_ArtboardLookup; std::vector<NestedArtboard*> m_NestedArtboards; + std::vector<FileAsset*> m_FileAssets; + std::vector<FileAssetReferencer*> m_FileAssetReferencers; int m_NextArtboardId; public: @@ -23,6 +27,8 @@ void addArtboard(Artboard* artboard); void addMissingArtboard(); void addNestedArtboard(NestedArtboard* artboard); + void addFileAsset(FileAsset* asset); + void addFileAssetReferencer(FileAssetReferencer* referencer); StatusCode resolve() override; const Backboard* backboard() const { return m_Backboard; }
diff --git a/include/rive/renderer.hpp b/include/rive/renderer.hpp index edb8810..f48d5ec 100644 --- a/include/rive/renderer.hpp +++ b/include/rive/renderer.hpp
@@ -38,6 +38,19 @@ virtual ~RenderPaint() {} }; + class RenderImage + { + protected: + int m_Width = 0; + int m_Height = 0; + + public: + virtual ~RenderImage() {} + virtual bool decode(const uint8_t* bytes) = 0; + int width() const { return m_Width; } + int height() const { return m_Height; } + }; + class RenderPath : public CommandPath { public: @@ -60,6 +73,8 @@ virtual void transform(const Mat2D& transform) = 0; virtual void drawPath(RenderPath* path, RenderPaint* paint) = 0; virtual void clipPath(RenderPath* path) = 0; + virtual void + drawImage(RenderImage* image, BlendMode value, float opacity) = 0; void computeAlignment(Mat2D& result, Fit fit, @@ -154,5 +169,6 @@ extern RenderPath* makeRenderPath(); extern RenderPaint* makeRenderPaint(); + extern RenderImage* makeRenderImage(); } // namespace rive #endif \ No newline at end of file
diff --git a/include/rive/shapes/image.hpp b/include/rive/shapes/image.hpp index c74c802..d36fcbd 100644 --- a/include/rive/shapes/image.hpp +++ b/include/rive/shapes/image.hpp
@@ -1,13 +1,20 @@ #ifndef _RIVE_IMAGE_HPP_ #define _RIVE_IMAGE_HPP_ #include "rive/generated/shapes/image_base.hpp" -#include <stdio.h> +#include "rive/assets/file_asset_referencer.hpp" namespace rive { - class Image : public ImageBase + class ImageAsset; + class Image : public ImageBase, public FileAssetReferencer { + private: + ImageAsset* m_ImageAsset = nullptr; + public: + ImageAsset* imageAsset() const { return m_ImageAsset; } void draw(Renderer* renderer) override; + StatusCode import(ImportStack& importStack) override; + void assets(const std::vector<FileAsset*>& assets) override; }; } // namespace rive
diff --git a/src/assets/file_asset.cpp b/src/assets/file_asset.cpp new file mode 100644 index 0000000..4779d15 --- /dev/null +++ b/src/assets/file_asset.cpp
@@ -0,0 +1,18 @@ +#include "rive/assets/file_asset.hpp" +#include "rive/backboard.hpp" +#include "rive/importers/backboard_importer.hpp" + +using namespace rive; + +StatusCode FileAsset::import(ImportStack& importStack) +{ + auto backboardImporter = + importStack.latest<BackboardImporter>(Backboard::typeKey); + if (backboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + backboardImporter->addFileAsset(this); + + return Super::import(importStack); +} \ No newline at end of file
diff --git a/src/assets/image_asset.cpp b/src/assets/image_asset.cpp new file mode 100644 index 0000000..22ae470 --- /dev/null +++ b/src/assets/image_asset.cpp
@@ -0,0 +1,9 @@ +#include "rive/assets/image_asset.hpp" +#include "rive/renderer.hpp" + +using namespace rive; + +ImageAsset::ImageAsset() : m_RenderImage(makeRenderImage()) {} + +ImageAsset::~ImageAsset() { delete m_RenderImage; } +void ImageAsset::decode(const uint8_t* bytes) { m_RenderImage->decode(bytes); } \ No newline at end of file
diff --git a/src/importers/backboard_importer.cpp b/src/importers/backboard_importer.cpp index b85adfc..0cf0f1b 100644 --- a/src/importers/backboard_importer.cpp +++ b/src/importers/backboard_importer.cpp
@@ -1,6 +1,7 @@ #include "rive/importers/backboard_importer.hpp" #include "rive/nested_artboard.hpp" +#include "rive/assets/file_asset_referencer.hpp" using namespace rive; @@ -13,6 +14,16 @@ m_NestedArtboards.push_back(artboard); } +void BackboardImporter::addFileAsset(FileAsset* asset) +{ + m_FileAssets.push_back(asset); +} + +void BackboardImporter::addFileAssetReferencer(FileAssetReferencer* referencer) +{ + m_FileAssetReferencers.push_back(referencer); +} + void BackboardImporter::addArtboard(Artboard* artboard) { m_ArtboardLookup[m_NextArtboardId++] = artboard; @@ -34,5 +45,10 @@ } } } + + for (auto referencer : m_FileAssetReferencers) + { + referencer->assets(m_FileAssets); + } return StatusCode::Ok; }
diff --git a/src/shapes/image.cpp b/src/shapes/image.cpp index dee6734..2256a1a 100644 --- a/src/shapes/image.cpp +++ b/src/shapes/image.cpp
@@ -1,4 +1,8 @@ #include "rive/shapes/image.hpp" +#include "rive/backboard.hpp" +#include "rive/importers/backboard_importer.hpp" +#include "rive/assets/file_asset.hpp" +#include "rive/assets/image_asset.hpp" using namespace rive; @@ -8,4 +12,32 @@ { return; } + renderer->drawImage( + m_ImageAsset->renderImage(), blendMode(), renderOpacity()); +} + +StatusCode Image::import(ImportStack& importStack) +{ + auto backboardImporter = + importStack.latest<BackboardImporter>(Backboard::typeKey); + if (backboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + backboardImporter->addFileAssetReferencer(this); + + return Super::import(importStack); +} + +void Image::assets(const std::vector<FileAsset*>& assets) +{ + if (assetId() < 0 || assetId() >= assets.size()) + { + return; + } + auto asset = assets[assetId()]; + if (asset->is<ImageAsset>()) + { + m_ImageAsset = asset->as<ImageAsset>(); + } } \ No newline at end of file
diff --git a/src/shapes/shape.cpp b/src/shapes/shape.cpp index 8e8767c..e9c1186 100644 --- a/src/shapes/shape.cpp +++ b/src/shapes/shape.cpp
@@ -82,7 +82,7 @@ // blend mode changes. for (auto paint : m_ShapePaints) { - paint->blendMode((BlendMode)blendModeValue()); + paint->blendMode(blendMode()); } }
diff --git a/test/assets/walle.riv b/test/assets/walle.riv new file mode 100644 index 0000000..248193a --- /dev/null +++ b/test/assets/walle.riv Binary files differ
diff --git a/test/image_asset_test.cpp b/test/image_asset_test.cpp new file mode 100644 index 0000000..61bc219 --- /dev/null +++ b/test/image_asset_test.cpp
@@ -0,0 +1,55 @@ +#include <rive/core/binary_reader.hpp> +#include <rive/file.hpp> +#include <rive/node.hpp> +#include <rive/shapes/clipping_shape.hpp> +#include <rive/shapes/rectangle.hpp> +#include <rive/shapes/image.hpp> +#include "no_op_renderer.hpp" +#include <catch.hpp> +#include <cstdio> + +TEST_CASE("image assets loads correctly", "[assets]") +{ + FILE* fp = fopen("../../test/assets/walle.riv", "r"); + REQUIRE(fp != nullptr); + + fseek(fp, 0, SEEK_END); + auto length = ftell(fp); + fseek(fp, 0, SEEK_SET); + uint8_t* bytes = new uint8_t[length]; + REQUIRE(fread(bytes, 1, length, fp) == length); + auto reader = rive::BinaryReader(bytes, length); + rive::File* file = nullptr; + auto result = rive::File::import(reader, &file); + + REQUIRE(result == rive::ImportResult::success); + REQUIRE(file != nullptr); + REQUIRE(file->artboard() != nullptr); + + auto node = file->artboard()->find("walle"); + REQUIRE(node != nullptr); + REQUIRE(node->is<rive::Image>()); + auto walle = node->as<rive::Image>(); + REQUIRE(walle->imageAsset() != nullptr); + + auto eve_left = file->artboard()->find("eve_left"); + REQUIRE(eve_left != nullptr); + REQUIRE(eve_left->is<rive::Image>()); + REQUIRE(eve_left->as<rive::Image>()->imageAsset() != nullptr); + + auto eve_right = file->artboard()->find("eve_right"); + REQUIRE(eve_right != nullptr); + REQUIRE(eve_right->is<rive::Image>()); + REQUIRE(eve_right->as<rive::Image>()->imageAsset() != nullptr); + REQUIRE(eve_right->as<rive::Image>()->imageAsset() != walle->imageAsset()); + REQUIRE(eve_right->as<rive::Image>()->imageAsset() == + eve_left->as<rive::Image>()->imageAsset()); + + file->artboard()->updateComponents(); + + rive::NoOpRenderer renderer; + file->artboard()->draw(&renderer); + + delete file; + delete[] bytes; +} \ No newline at end of file
diff --git a/test/no_op_renderer.cpp b/test/no_op_renderer.cpp index 51d91c2..5ef624b 100644 --- a/test/no_op_renderer.cpp +++ b/test/no_op_renderer.cpp
@@ -5,4 +5,5 @@ { RenderPaint* makeRenderPaint() { return new NoOpRenderPaint(); } RenderPath* makeRenderPath() { return new NoOpRenderPath(); } + RenderImage* makeRenderImage() { return new NoOpRenderImage(); } } // namespace rive \ No newline at end of file
diff --git a/test/no_op_renderer.hpp b/test/no_op_renderer.hpp index f1716ac..b7ad368 100644 --- a/test/no_op_renderer.hpp +++ b/test/no_op_renderer.hpp
@@ -5,6 +5,12 @@ namespace rive { + class NoOpRenderImage : public RenderImage + { + public: + bool decode(const uint8_t* bytes) override { return true; } + }; + class NoOpRenderPaint : public RenderPaint { public: @@ -90,12 +96,15 @@ class NoOpRenderer : public Renderer { - void save() {} - void restore() {} - void transform(const Mat2D& transform) {} - void translate(float x, float y) {} - void drawPath(RenderPath* path, RenderPaint* paint) {} - void clipPath(RenderPath* path) {} + void save() override {} + void restore() override {} + void transform(const Mat2D& transform) override {} + void drawPath(RenderPath* path, RenderPaint* paint) override {} + void clipPath(RenderPath* path) override {} + void + drawImage(RenderImage* image, BlendMode value, float opacity) override + { + } }; } // namespace rive