|
4 | 4 | #include "rive/importers/backboard_importer.hpp" |
5 | 5 | #include "rive/assets/file_asset.hpp" |
6 | 6 | #include "rive/assets/image_asset.hpp" |
| 7 | +#include "rive/layout.hpp" |
7 | 8 | #include "rive/layout/n_slicer.hpp" |
8 | 9 | #include "rive/shapes/mesh_drawable.hpp" |
9 | 10 | #include "rive/artboard.hpp" |
@@ -135,7 +136,15 @@ Core* Image::clone() const |
135 | 136 | return twin; |
136 | 137 | } |
137 | 138 |
|
138 | | -void Image::setMesh(MeshDrawable* mesh) { m_Mesh = mesh; } |
| 139 | +void Image::setMesh(MeshDrawable* mesh) |
| 140 | +{ |
| 141 | + if (m_Mesh == mesh) |
| 142 | + { |
| 143 | + return; |
| 144 | + } |
| 145 | + m_Mesh = mesh; |
| 146 | + updateImageScale(); |
| 147 | +} |
139 | 148 |
|
140 | 149 | float Image::width() const |
141 | 150 | { |
@@ -218,27 +227,119 @@ void Image::controlSize(Vec2D size, |
218 | 227 | } |
219 | 228 | } |
220 | 229 |
|
| 230 | +void Image::updateTransform() |
| 231 | +{ |
| 232 | + Super::updateTransform(); |
| 233 | + m_Transform[4] += m_layoutOffsetX; |
| 234 | + m_Transform[5] += m_layoutOffsetY; |
| 235 | +} |
| 236 | + |
221 | 237 | void Image::updateImageScale() |
222 | 238 | { |
223 | | - // User-created meshes are not affected by scale |
224 | | - if ((m_Mesh != nullptr && m_Mesh->type() == MeshType::vertex) || |
225 | | - imageAsset() == nullptr) |
| 239 | + if (imageAsset() == nullptr) |
226 | 240 | { |
| 241 | + if (m_layoutOffsetX != 0.0f || m_layoutOffsetY != 0.0f) |
| 242 | + { |
| 243 | + m_layoutOffsetX = 0.0f; |
| 244 | + m_layoutOffsetY = 0.0f; |
| 245 | + markTransformDirty(); |
| 246 | + } |
227 | 247 | return; |
228 | 248 | } |
| 249 | + |
| 250 | + float newOffsetX = 0.0f; |
| 251 | + float newOffsetY = 0.0f; |
229 | 252 | auto renderImage = imageAsset()->renderImage(); |
230 | 253 | if (renderImage != nullptr && !std::isnan(m_layoutWidth) && |
231 | 254 | !std::isnan(m_layoutHeight)) |
232 | 255 | { |
233 | | - float newScaleX = m_layoutWidth / (float)renderImage->width(); |
234 | | - float newScaleY = m_layoutHeight / (float)renderImage->height(); |
| 256 | + float imgW = (float)renderImage->width(); |
| 257 | + float imgH = (float)renderImage->height(); |
| 258 | + float newScaleX, newScaleY; |
| 259 | + auto imageFit = static_cast<Fit>(fit()); |
| 260 | + switch (imageFit) |
| 261 | + { |
| 262 | + case Fit::fill: |
| 263 | + newScaleX = m_layoutWidth / imgW; |
| 264 | + newScaleY = m_layoutHeight / imgH; |
| 265 | + break; |
| 266 | + case Fit::contain: |
| 267 | + { |
| 268 | + float s = |
| 269 | + std::fmin(m_layoutWidth / imgW, m_layoutHeight / imgH); |
| 270 | + newScaleX = newScaleY = s; |
| 271 | + break; |
| 272 | + } |
| 273 | + case Fit::cover: |
| 274 | + { |
| 275 | + float s = |
| 276 | + std::fmax(m_layoutWidth / imgW, m_layoutHeight / imgH); |
| 277 | + newScaleX = newScaleY = s; |
| 278 | + break; |
| 279 | + } |
| 280 | + case Fit::fitWidth: |
| 281 | + newScaleX = newScaleY = m_layoutWidth / imgW; |
| 282 | + break; |
| 283 | + case Fit::fitHeight: |
| 284 | + newScaleX = newScaleY = m_layoutHeight / imgH; |
| 285 | + break; |
| 286 | + case Fit::none: |
| 287 | + newScaleX = newScaleY = 1.0f; |
| 288 | + break; |
| 289 | + case Fit::scaleDown: |
| 290 | + { |
| 291 | + float s = |
| 292 | + std::fmin(m_layoutWidth / imgW, m_layoutHeight / imgH); |
| 293 | + s = s < 1.0f ? s : 1.0f; |
| 294 | + newScaleX = newScaleY = s; |
| 295 | + break; |
| 296 | + } |
| 297 | + case Fit::layout: |
| 298 | + default: |
| 299 | + newScaleX = m_layoutWidth / imgW; |
| 300 | + newScaleY = m_layoutHeight / imgH; |
| 301 | + break; |
| 302 | + } |
| 303 | + |
| 304 | + // Compatibility: legacy files assume fill does not apply fit/alignment |
| 305 | + // translation offsets, only scale. |
| 306 | + if (imageFit != Fit::fill) |
| 307 | + { |
| 308 | + float boundsW = imgW; |
| 309 | + float boundsH = imgH; |
| 310 | + float boundsLeft = -imgW * originX(); |
| 311 | + float boundsTop = -imgH * originY(); |
| 312 | + if (m_Mesh != nullptr && m_Mesh->type() == MeshType::vertex) |
| 313 | + { |
| 314 | + // Keep fit behavior stable while editing vertex meshes. |
| 315 | + boundsLeft = -imgW * 0.5f; |
| 316 | + boundsTop = -imgH * 0.5f; |
| 317 | + } |
| 318 | + Alignment alignment(alignmentX(), alignmentY()); |
| 319 | + float xAlign = (alignment.x() + 1.0f) * 0.5f; |
| 320 | + float yAlign = (alignment.y() + 1.0f) * 0.5f; |
| 321 | + float scaledLeft = boundsLeft * newScaleX; |
| 322 | + float scaledTop = boundsTop * newScaleY; |
| 323 | + float widthRemainder = m_layoutWidth - (boundsW * newScaleX); |
| 324 | + float heightRemainder = m_layoutHeight - (boundsH * newScaleY); |
| 325 | + newOffsetX = -scaledLeft + widthRemainder * xAlign; |
| 326 | + newOffsetY = -scaledTop + heightRemainder * yAlign; |
| 327 | + } |
| 328 | + |
235 | 329 | if (newScaleX != scaleX() || newScaleY != scaleY()) |
236 | 330 | { |
237 | 331 | scaleX(newScaleX); |
238 | 332 | scaleY(newScaleY); |
239 | | - addDirt(ComponentDirt::WorldTransform, false); |
240 | 333 | } |
241 | 334 | } |
| 335 | + if (newOffsetX != m_layoutOffsetX || newOffsetY != m_layoutOffsetY) |
| 336 | + { |
| 337 | + m_layoutOffsetX = newOffsetX; |
| 338 | + m_layoutOffsetY = newOffsetY; |
| 339 | + // Offset is applied in updateTransform(), so changing it must mark the |
| 340 | + // local transform dirty (not just world transform). |
| 341 | + markTransformDirty(); |
| 342 | + } |
242 | 343 | } |
243 | 344 |
|
244 | 345 | AABB Image::localBounds() const |
|
0 commit comments