aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkRegion.h1
-rw-r--r--src/core/SkBlitter.cpp11
-rw-r--r--src/core/SkRegion.cpp43
-rw-r--r--src/core/SkRegionPriv.h10
-rw-r--r--tests/RegionTest.cpp15
5 files changed, 73 insertions, 7 deletions
diff --git a/include/core/SkRegion.h b/include/core/SkRegion.h
index 84ab670cc3..a3aaee1cbe 100644
--- a/include/core/SkRegion.h
+++ b/include/core/SkRegion.h
@@ -457,6 +457,7 @@ private:
friend struct RunHead;
friend class Iterator;
friend class Spanerator;
+ friend class SkRegionPriv;
friend class SkRgnBuilder;
friend class SkFlatRegion;
};
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index 24494c1e71..ccc31d093b 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -15,6 +15,7 @@
#include "SkMask.h"
#include "SkMaskFilterBase.h"
#include "SkPaintPriv.h"
+#include "SkRegionPriv.h"
#include "SkShaderBase.h"
#include "SkString.h"
#include "SkTLazy.h"
@@ -343,13 +344,9 @@ void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) {
}
void SkBlitter::blitRegion(const SkRegion& clip) {
- SkRegion::Iterator iter(clip);
-
- while (!iter.done()) {
- const SkIRect& cr = iter.rect();
- this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
- iter.next();
- }
+ SkRegionPriv::VisitSpans(clip, [this](const SkIRect& r) {
+ this->blitRect(r.left(), r.top(), r.width(), r.height());
+ });
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp
index 3a1a63e187..8ba3167297 100644
--- a/src/core/SkRegion.cpp
+++ b/src/core/SkRegion.cpp
@@ -1503,6 +1503,49 @@ bool SkRegion::Spanerator::next(int* left, int* right) {
return true;
}
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void visit_pairs(int pairCount, int y, const int32_t pairs[],
+ const std::function<void(const SkIRect&)>& visitor) {
+ for (int i = 0; i < pairCount; ++i) {
+ visitor({ pairs[0], y, pairs[1], y + 1 });
+ pairs += 2;
+ }
+}
+
+void SkRegionPriv::VisitSpans(const SkRegion& rgn,
+ const std::function<void(const SkIRect&)>& visitor) {
+ if (rgn.isEmpty()) {
+ return;
+ }
+ if (rgn.isRect()) {
+ visitor(rgn.getBounds());
+ } else {
+ const int32_t* p = rgn.fRunHead->readonly_runs();
+ int32_t top = *p++;
+ int32_t bot = *p++;
+ do {
+ int pairCount = *p++;
+ if (pairCount == 1) {
+ visitor({ p[0], top, p[1], bot });
+ p += 2;
+ } else if (pairCount > 1) {
+ // we have to loop repeated in Y, sending each interval in Y -> X order
+ for (int y = top; y < bot; ++y) {
+ visit_pairs(pairCount, y, p, visitor);
+ }
+ p += pairCount * 2;
+ }
+ assert_sentinel(*p, true);
+ p += 1; // skip sentinel
+
+ // read next bottom or sentinel
+ top = bot;
+ bot = *p++;
+ } while (!SkRegionValueIsSentinel(bot));
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
#ifdef SK_DEBUG
diff --git a/src/core/SkRegionPriv.h b/src/core/SkRegionPriv.h
index 4ccb68a392..ce21e92960 100644
--- a/src/core/SkRegionPriv.h
+++ b/src/core/SkRegionPriv.h
@@ -244,4 +244,14 @@ private:
int32_t fIntervalCount;
};
+#include <functional>
+
+class SkRegionPriv {
+public:
+ // Call the function with each span, in Y -> X ascending order.
+ // We pass a rect, but we will still ensure the span Y->X ordering, so often the height
+ // of the rect may be 1. It should never be empty.
+ static void VisitSpans(const SkRegion& rgn, const std::function<void(const SkIRect&)>&);
+};
+
#endif
diff --git a/tests/RegionTest.cpp b/tests/RegionTest.cpp
index be84d14c14..422e06e968 100644
--- a/tests/RegionTest.cpp
+++ b/tests/RegionTest.cpp
@@ -419,3 +419,18 @@ DEF_TEST(region_toobig, reporter) {
REPORTER_ASSERT(reporter, rgn.isEmpty());
}
+DEF_TEST(region_inverse_union_skbug_7491, reporter) {
+ SkPath path;
+ path.setFillType(SkPath::kInverseWinding_FillType);
+ path.moveTo(10, 20); path.lineTo(10, 30); path.lineTo(10.1f, 10); path.close();
+
+ SkRegion clip;
+ clip.op(SkIRect::MakeLTRB(10, 10, 15, 20), SkRegion::kUnion_Op);
+ clip.op(SkIRect::MakeLTRB(20, 10, 25, 20), SkRegion::kUnion_Op);
+
+ SkRegion rgn;
+ rgn.setPath(path, clip);
+
+ REPORTER_ASSERT(reporter, clip == rgn);
+}
+