diff options
-rw-r--r-- | include/core/SkRegion.h | 1 | ||||
-rw-r--r-- | src/core/SkBlitter.cpp | 11 | ||||
-rw-r--r-- | src/core/SkRegion.cpp | 43 | ||||
-rw-r--r-- | src/core/SkRegionPriv.h | 10 | ||||
-rw-r--r-- | tests/RegionTest.cpp | 15 |
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); +} + |