Merge pull request #29069 from perrylin4:fix/11988-rect-unsigned-intersection

core: fix unsigned Rect intersection for disjoint rectangles (#11988) - #29069

Fixes #11988

## Problem
`Rect_<_Tp>::operator&` / `operator&=` returned a non-empty rectangle when two **unsigned** rectangles do not overlap.

### Example:
```cpp
cv::Rect_<unsigned> r1(0, 0, 1, 1);
cv::Rect_<unsigned> r2(2, 2, 1, 1);
auto inter = r1 & r2;  // was [1 x 1 from (2, 2)], expected empty
```
Root cause: the previous implementation subtracted edge coordinates before checking overlap. For `unsigned _Tp`, expressions like `width - (x_max - x_min)` can underflow when rectangles are disjoint.

### Solution
Add a check for underflow

### Tests
Added regression test `Core_Rect.test_unsigned_overflow in modules/core/test/test_misc.cpp`.
This commit is contained in:
perry_lin
2026-08-30 17:04:25 +08:00
committed by GitHub
parent 755546643a
commit 88f2f459db
2 changed files with 20 additions and 0 deletions

View File

@@ -2012,6 +2012,15 @@ Rect_<_Tp>& operator &= ( Rect_<_Tp>& a, const Rect_<_Tp>& b )
a = Rect_<_Tp>();
return a;
}
// If the delta between x/y coordinates exceeds the corresponding width/height,
// the rectangles cannot overlap and `width - delta` would underflow for unsigned types.
if (Rx_min.width < (Rx_max.x - Rx_min.x) ||
Ry_min.height < (Ry_max.y - Ry_min.y)) {
a = Rect_<_Tp>();
return a;
}
// We now know that either Rx_min.x >= 0, or
// Rx_min.x < 0 && Rx_min.x + Rx_min.width >= Rx_max.x and therefore
// Rx_min.width >= (Rx_max.x - Rx_min.x) which means (Rx_max.x - Rx_min.x)

View File

@@ -996,6 +996,17 @@ REGISTER_TYPED_TEST_CASE_P(Rect_Test, Overflows, OnTheEdge);
typedef ::testing::Types<int, float, double> RectTypes;
INSTANTIATE_TYPED_TEST_CASE_P(Negative_Test, Rect_Test, RectTypes);
TEST(Core_Rect, test_unsigned_overflow_11988)
{
typedef Rect_<unsigned> R;
R r1(0, 0, 1u, 1u);
R r2(2u, 2u, 1u, 1u);
auto inter = r1 & r2;
EXPECT_EQ(R(), inter);
EXPECT_EQ(0u, inter.area());
EXPECT_TRUE(inter.empty());
}
// Expected that SkipTestException thrown in the constructor should skip test but not fail
struct TestFixtureSkip: public ::testing::Test {
TestFixtureSkip(bool throwEx = true) {