Skip to content

Commit

Permalink
Fix crash on controlSize when image not yet available
Browse files Browse the repository at this point in the history
Fixes the crash reported here https://2dimensions.slack.com/archives/C078KG6KAJY/p1729725850289449.

For referenced images, we need to store off the layout size and update its size after the image has loaded, so we have its width and height in order to properly scale it to the layout's bounds.

Diffs=
4fec171eac Fix crash on controlSize when image not yet available (#8414)

Co-authored-by: Philip Chung <philterdesign@gmail.com>
  • Loading branch information
philter and philter committed Oct 26, 2024
1 parent 14a20b0 commit e1e1db8
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bb3b386c1515ec9e14d7ca583b62da24d7f33a88
4fec171eac63c83a5c0a6d7abb3df0945d82775c
6 changes: 6 additions & 0 deletions include/rive/assets/image_asset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "rive/generated/assets/image_asset_base.hpp"
#include "rive/renderer.hpp"
#include "rive/simple_array.hpp"
#include <functional>
#include <string>

namespace rive
Expand All @@ -12,6 +13,7 @@ class ImageAsset : public ImageAssetBase
{
private:
rcp<RenderImage> m_RenderImage;
std::function<void()> m_imageReadyCallback = nullptr;

public:
ImageAsset() {}
Expand All @@ -24,6 +26,10 @@ class ImageAsset : public ImageAssetBase
std::string fileExtension() const override;
RenderImage* renderImage() const { return m_RenderImage.get(); }
void renderImage(rcp<RenderImage> renderImage);
void onImageReady(std::function<void()> callback)
{
m_imageReadyCallback = callback;
}
};
} // namespace rive

Expand Down
5 changes: 5 additions & 0 deletions include/rive/shapes/image.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ class Image : public ImageBase, public FileAssetReferencer
{
private:
MeshDrawable* m_Mesh = nullptr;
// Since layouts only pass down width/height we store those
// and use the image width/height to compute the proper scale
float m_layoutWidth = NAN;
float m_layoutHeight = NAN;
void updateImageScale();

public:
void setMesh(MeshDrawable* mesh);
Expand Down
4 changes: 4 additions & 0 deletions src/assets/image_asset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ bool ImageAsset::decode(SimpleArray<uint8_t>& data, Factory* factory)
decodedByteSize = data.size();
#endif
renderImage(factory->decodeImage(data));
if (m_imageReadyCallback != nullptr)
{
m_imageReadyCallback();
}
return m_RenderImage != nullptr;
}

Expand Down
33 changes: 26 additions & 7 deletions src/shapes/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ void Image::setAsset(FileAsset* asset)
{
m_Mesh->onAssetLoaded(imageAsset()->renderImage());
}
asset->as<ImageAsset>()->onImageReady([&]() { updateImageScale(); });
updateImageScale();
}
}

Expand Down Expand Up @@ -189,14 +191,31 @@ Vec2D Image::measureLayout(float width,

void Image::controlSize(Vec2D size)
{
auto renderImage = imageAsset()->renderImage();
auto newScaleX = size.x / renderImage->width();
auto newScaleY = size.y / renderImage->height();
if (newScaleX != scaleX() || newScaleY != scaleY())
// We store layout width/height because the image asset may not be available
// yet (referenced images) and we have defer controlling its size
if (m_layoutWidth != size.x || m_layoutHeight != size.y)
{
scaleX(newScaleX);
scaleY(newScaleY);
addDirt(ComponentDirt::WorldTransform, false);
m_layoutWidth = size.x;
m_layoutHeight = size.y;

updateImageScale();
}
}

void Image::updateImageScale()
{
if (imageAsset() != nullptr && imageAsset()->renderImage() != nullptr &&
!std::isnan(m_layoutWidth) && !std::isnan(m_layoutHeight))
{
auto renderImage = imageAsset()->renderImage();
auto newScaleX = m_layoutWidth / renderImage->width();
auto newScaleY = m_layoutHeight / renderImage->height();
if (newScaleX != scaleX() || newScaleY != scaleY())
{
scaleX(newScaleX);
scaleY(newScaleY);
addDirt(ComponentDirt::WorldTransform, false);
}
}
}

Expand Down

0 comments on commit e1e1db8

Please sign in to comment.