[Impeller] Make IsEmpty methods on Rect and Size NaN-aware (#47725)
Removes unused or redundant overloads and fixes the IsEmpty method on Size to be both simpler (2 comparisons vs. 4) and NaN-aware. Rect automatically uses the new code via delegation to the Size object.
diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files
index 3981ef1..18a25c8 100644
--- a/ci/licenses_golden/excluded_files
+++ b/ci/licenses_golden/excluded_files
@@ -150,6 +150,7 @@
../../../flutter/impeller/geometry/geometry_unittests.cc
../../../flutter/impeller/geometry/matrix_unittests.cc
../../../flutter/impeller/geometry/rect_unittests.cc
+../../../flutter/impeller/geometry/size_unittests.cc
../../../flutter/impeller/golden_tests/README.md
../../../flutter/impeller/golden_tests_harvester/.dart_tool
../../../flutter/impeller/golden_tests_harvester/.gitignore
diff --git a/impeller/core/texture_descriptor.h b/impeller/core/texture_descriptor.h
index feb012b..aa1b574 100644
--- a/impeller/core/texture_descriptor.h
+++ b/impeller/core/texture_descriptor.h
@@ -83,7 +83,7 @@
constexpr bool IsValid() const {
return format != PixelFormat::kUnknown && //
- size.IsPositive() && //
+ !size.IsEmpty() && //
mip_count >= 1u && //
SamplingOptionsAreValid();
}
diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn
index 0ce6382..2ee3221 100644
--- a/impeller/geometry/BUILD.gn
+++ b/impeller/geometry/BUILD.gn
@@ -63,6 +63,7 @@
"geometry_unittests.cc",
"matrix_unittests.cc",
"rect_unittests.cc",
+ "size_unittests.cc",
]
deps = [
diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h
index c40ce26..fa33757 100644
--- a/impeller/geometry/rect.h
+++ b/impeller/geometry/rect.h
@@ -123,8 +123,7 @@
return Union(o).size == size;
}
- constexpr bool IsZero() const { return size.IsZero(); }
-
+ /// Returns true if either of the width or height are 0, negative, or NaN.
constexpr bool IsEmpty() const { return size.IsEmpty(); }
constexpr bool IsMaximum() const { return *this == MakeMaximum(); }
diff --git a/impeller/geometry/rect_unittests.cc b/impeller/geometry/rect_unittests.cc
index 583c04f..27bedfa 100644
--- a/impeller/geometry/rect_unittests.cc
+++ b/impeller/geometry/rect_unittests.cc
@@ -55,5 +55,46 @@
}
}
+TEST(SizeTest, RectIsEmpty) {
+ auto nan = std::numeric_limits<Scalar>::quiet_NaN();
+
+ // Non-empty
+ EXPECT_FALSE(Rect::MakeXYWH(1.5, 2.3, 10.5, 7.2).IsEmpty());
+
+ // Empty both width and height both 0 or negative, in all combinations
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 0.0, 0.0).IsEmpty());
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, -1.0, -1.0).IsEmpty());
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 0.0, -1.0).IsEmpty());
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, -1.0, 0.0).IsEmpty());
+
+ // Empty for 0 or negative width or height (but not both at the same time)
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 10.5, 0.0).IsEmpty());
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 10.5, -1.0).IsEmpty());
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 0.0, 7.2).IsEmpty());
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, -1.0, 7.2).IsEmpty());
+
+ // Empty for NaN in width or height or both
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 10.5, nan).IsEmpty());
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, nan, 7.2).IsEmpty());
+ EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, nan, nan).IsEmpty());
+}
+
+TEST(SizeTest, IRectIsEmpty) {
+ // Non-empty
+ EXPECT_FALSE(IRect::MakeXYWH(1, 2, 10, 7).IsEmpty());
+
+ // Empty both width and height both 0 or negative, in all combinations
+ EXPECT_TRUE(IRect::MakeXYWH(1, 2, 0, 0).IsEmpty());
+ EXPECT_TRUE(IRect::MakeXYWH(1, 2, -1, -1).IsEmpty());
+ EXPECT_TRUE(IRect::MakeXYWH(1, 2, -1, 0).IsEmpty());
+ EXPECT_TRUE(IRect::MakeXYWH(1, 2, 0, -1).IsEmpty());
+
+ // Empty for 0 or negative width or height (but not both at the same time)
+ EXPECT_TRUE(IRect::MakeXYWH(1, 2, 10, 0).IsEmpty());
+ EXPECT_TRUE(IRect::MakeXYWH(1, 2, 10, -1).IsEmpty());
+ EXPECT_TRUE(IRect::MakeXYWH(1, 2, 0, 7).IsEmpty());
+ EXPECT_TRUE(IRect::MakeXYWH(1, 2, -1, 7).IsEmpty());
+}
+
} // namespace testing
} // namespace impeller
diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h
index 680e205..7ee34d1 100644
--- a/impeller/geometry/size.h
+++ b/impeller/geometry/size.h
@@ -96,13 +96,8 @@
constexpr Type Area() const { return width * height; }
- constexpr bool IsPositive() const { return width > 0 && height > 0; }
-
- constexpr bool IsNegative() const { return width < 0 || height < 0; }
-
- constexpr bool IsZero() const { return width == 0 || height == 0; }
-
- constexpr bool IsEmpty() const { return IsNegative() || IsZero(); }
+ /// Returns true if either of the width or height are 0, negative, or NaN.
+ constexpr bool IsEmpty() const { return !(width > 0 && height > 0); }
template <class U>
static constexpr TSize Ceil(const TSize<U>& other) {
@@ -112,7 +107,7 @@
constexpr size_t MipCount() const {
constexpr size_t minimum_mip = 1u;
- if (!IsPositive()) {
+ if (IsEmpty()) {
return minimum_mip;
}
size_t result = std::max(ceil(log2(width)), ceil(log2(height)));
diff --git a/impeller/geometry/size_unittests.cc b/impeller/geometry/size_unittests.cc
new file mode 100644
index 0000000..df8d26b
--- /dev/null
+++ b/impeller/geometry/size_unittests.cc
@@ -0,0 +1,54 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gtest/gtest.h"
+
+#include "flutter/impeller/geometry/size.h"
+
+namespace impeller {
+namespace testing {
+
+TEST(SizeTest, SizeIsEmpty) {
+ auto nan = std::numeric_limits<Scalar>::quiet_NaN();
+
+ // Non-empty
+ EXPECT_FALSE(Size(10.5, 7.2).IsEmpty());
+
+ // Empty both width and height both 0 or negative, in all combinations
+ EXPECT_TRUE(Size(0.0, 0.0).IsEmpty());
+ EXPECT_TRUE(Size(-1.0, -1.0).IsEmpty());
+ EXPECT_TRUE(Size(-1.0, 0.0).IsEmpty());
+ EXPECT_TRUE(Size(0.0, -1.0).IsEmpty());
+
+ // Empty for 0 or negative width or height (but not both at the same time)
+ EXPECT_TRUE(Size(10.5, 0.0).IsEmpty());
+ EXPECT_TRUE(Size(10.5, -1.0).IsEmpty());
+ EXPECT_TRUE(Size(0.0, 7.2).IsEmpty());
+ EXPECT_TRUE(Size(-1.0, 7.2).IsEmpty());
+
+ // Empty for NaN in width or height or both
+ EXPECT_TRUE(Size(10.5, nan).IsEmpty());
+ EXPECT_TRUE(Size(nan, 7.2).IsEmpty());
+ EXPECT_TRUE(Size(nan, nan).IsEmpty());
+}
+
+TEST(SizeTest, ISizeIsEmpty) {
+ // Non-empty
+ EXPECT_FALSE(ISize(10, 7).IsEmpty());
+
+ // Empty both width and height both 0 or negative, in all combinations
+ EXPECT_TRUE(ISize(0, 0).IsEmpty());
+ EXPECT_TRUE(ISize(-1, -1).IsEmpty());
+ EXPECT_TRUE(ISize(-1, 0).IsEmpty());
+ EXPECT_TRUE(ISize(0, -1).IsEmpty());
+
+ // Empty for 0 or negative width or height (but not both at the same time)
+ EXPECT_TRUE(ISize(10, 0).IsEmpty());
+ EXPECT_TRUE(ISize(10, -1).IsEmpty());
+ EXPECT_TRUE(ISize(0, 7).IsEmpty());
+ EXPECT_TRUE(ISize(-1, 7).IsEmpty());
+}
+
+} // namespace testing
+} // namespace impeller
diff --git a/impeller/image/decompressed_image.cc b/impeller/image/decompressed_image.cc
index c31c184..396aada 100644
--- a/impeller/image/decompressed_image.cc
+++ b/impeller/image/decompressed_image.cc
@@ -18,7 +18,7 @@
Format format,
std::shared_ptr<const fml::Mapping> allocation)
: size_(size), format_(format), allocation_(std::move(allocation)) {
- if (!allocation_ || !size.IsPositive() || format_ == Format::kInvalid) {
+ if (!allocation_ || size.IsEmpty() || format_ == Format::kInvalid) {
return;
}
is_valid_ = true;
diff --git a/impeller/renderer/snapshot.cc b/impeller/renderer/snapshot.cc
index a73a210..b55a971 100644
--- a/impeller/renderer/snapshot.cc
+++ b/impeller/renderer/snapshot.cc
@@ -16,7 +16,7 @@
}
std::optional<Matrix> Snapshot::GetUVTransform() const {
- if (!texture || texture->GetSize().IsZero()) {
+ if (!texture || texture->GetSize().IsEmpty()) {
return std::nullopt;
}
return Matrix::MakeScale(1 / Vector2(texture->GetSize())) *