Fix cursor hotspot out of bounds when flipping (#17571)

# Objective

- Fix off by one error introduced in
https://github.com/bevyengine/bevy/pull/17540 causing:

```
Cursor image StrongHandle<Image>{ id: Index(AssetIndex { generation: 0, index: 3 }), path: Some(cursors/kenney_crosshairPack/Tilesheet/crosshairs_tilesheet_white.png) } is invalid: The specified hotspot (64, 64) is outside the image bounds (64x64).
```

- First PR commit and run shows the bug:
https://github.com/bevyengine/bevy/actions/runs/13009405866/job/36283507530?pr=17571
- Second PR commit fixes it.

## Solution

- Hotspot coordinates are 0-indexed, so we need to subtract 1 from the
width and height.

## Testing

- Fix the tests which included the off-by-one error in their expected
values.
- Consolidate the tests into a single test for brevity.
- Test round trip transform to ensure we can "undo" to get back to the
original value.
- Add a specific bounds test.
- Ran the example again and observed there are no more error logs:
`cargo run --example custom_cursor_image --features=custom_cursor`.
This commit is contained in:
mgi388 2025-02-03 05:22:34 +11:00 committed by GitHub
parent 469b218f20
commit 756948e311
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -204,11 +204,16 @@ pub(crate) fn transform_hotspot(
) -> (u16, u16) { ) -> (u16, u16) {
let hotspot_x = hotspot.0 as f32; let hotspot_x = hotspot.0 as f32;
let hotspot_y = hotspot.1 as f32; let hotspot_y = hotspot.1 as f32;
let (width, height) = (rect.width(), rect.height()); let (width, height) = (rect.width(), rect.height());
let hotspot_x = if flip_x { width - hotspot_x } else { hotspot_x }; let hotspot_x = if flip_x {
(width - 1.0).max(0.0) - hotspot_x
} else {
hotspot_x
};
let hotspot_y = if flip_y { let hotspot_y = if flip_y {
height - hotspot_y (height - 1.0).max(0.0) - hotspot_y
} else { } else {
hotspot_y hotspot_y
}; };
@ -576,46 +581,26 @@ mod tests {
); );
#[test] #[test]
fn test_transform_hotspot_no_flip() { fn test_transform_hotspot() {
let hotspot = (10, 20); fn test(hotspot: (u16, u16), flip_x: bool, flip_y: bool, rect: Rect, expected: (u16, u16)) {
let rect = Rect { let transformed = transform_hotspot(hotspot, flip_x, flip_y, rect);
min: Vec2::ZERO, assert_eq!(transformed, expected);
max: Vec2::new(100.0, 200.0),
};
let transformed = transform_hotspot(hotspot, false, false, rect);
assert_eq!(transformed, (10, 20));
}
#[test] // Round-trip test: Applying the same transformation again should
fn test_transform_hotspot_flip_x() { // reverse it.
let hotspot = (10, 20); let transformed = transform_hotspot(transformed, flip_x, flip_y, rect);
let rect = Rect { assert_eq!(transformed, hotspot);
min: Vec2::ZERO, }
max: Vec2::new(100.0, 200.0),
};
let transformed = transform_hotspot(hotspot, true, false, rect);
assert_eq!(transformed, (90, 20));
}
#[test]
fn test_transform_hotspot_flip_y() {
let hotspot = (10, 20);
let rect = Rect { let rect = Rect {
min: Vec2::ZERO, min: Vec2::ZERO,
max: Vec2::new(100.0, 200.0), max: Vec2::new(100.0, 200.0),
}; };
let transformed = transform_hotspot(hotspot, false, true, rect);
assert_eq!(transformed, (10, 180));
}
#[test] test((10, 20), false, false, rect, (10, 20)); // no flip
fn test_transform_hotspot_flip_both() { test((10, 20), true, false, rect, (89, 20)); // flip X
let hotspot = (10, 20); test((10, 20), false, true, rect, (10, 179)); // flip Y
let rect = Rect { test((10, 20), true, true, rect, (89, 179)); // flip both
min: Vec2::ZERO, test((0, 0), true, true, rect, (99, 199)); // flip both (bounds check)
max: Vec2::new(100.0, 200.0),
};
let transformed = transform_hotspot(hotspot, true, true, rect);
assert_eq!(transformed, (90, 180));
} }
} }