aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkPath.h36
-rw-r--r--include/core/SkPathRef.h65
-rw-r--r--src/core/SkPath.cpp73
-rw-r--r--src/core/SkPathRef.cpp127
-rw-r--r--tests/RRectInPathTest.cpp388
5 files changed, 600 insertions, 89 deletions
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 3ff0af15a2..3c68ff7d84 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -154,6 +154,17 @@ public:
*/
bool isOval(SkRect* rect) const { return fPathRef->isOval(rect); }
+ /** Returns true if the path is a round rect.
+ *
+ * @param rrect Returns the bounding rect and radii of this round rect.
+ *
+ * @return true if this path is a round rect.
+ * Tracking whether a path is a round rect is considered an
+ * optimization for performance and so some paths that are in
+ * fact round rects can report false.
+ */
+ bool isRRect(SkRRect* rrect) const { return fPathRef->isRRect(rrect); }
+
/** Clear any lines and curves from the path, making it empty. This frees up
internal storage associated with those segments.
On Android, does not change fSourcePath.
@@ -918,10 +929,14 @@ public:
*/
class SK_API RawIter {
public:
- RawIter();
- RawIter(const SkPath&);
+ RawIter() {}
+ RawIter(const SkPath& path) {
+ setPath(path);
+ }
- void setPath(const SkPath&);
+ void setPath(const SkPath& path) {
+ fRawIter.setPathRef(*path.fPathRef.get());
+ }
/** Return the next verb in this iteration of the path. When all
segments have been visited, return kDone_Verb.
@@ -930,15 +945,17 @@ public:
This must not be NULL.
@return The verb for the current segment
*/
- Verb next(SkPoint pts[4]);
+ Verb next(SkPoint pts[4]) {
+ return (Verb) fRawIter.next(pts);
+ }
- SkScalar conicWeight() const { return *fConicWeights; }
+ SkScalar conicWeight() const {
+ return fRawIter.conicWeight();
+ }
private:
- const SkPoint* fPts;
- const uint8_t* fVerbs;
- const uint8_t* fVerbStop;
- const SkScalar* fConicWeights;
+ SkPathRef::Iter fRawIter;
+ friend class SkPath;
};
/**
@@ -1066,6 +1083,7 @@ private:
friend class SkAutoDisableDirectionCheck;
friend class SkBench_AddPathTest; // perf test reversePathTo
friend class PathTest_Private; // unit test reversePathTo
+ friend class ForceIsRRect_Private; // unit test isRRect
};
#endif
diff --git a/include/core/SkPathRef.h b/include/core/SkPathRef.h
index c09f6e87e4..86f55c9bca 100644
--- a/include/core/SkPathRef.h
+++ b/include/core/SkPathRef.h
@@ -11,6 +11,7 @@
#include "SkMatrix.h"
#include "SkPoint.h"
+#include "SkRRect.h"
#include "SkRect.h"
#include "SkRefCnt.h"
#include "SkTDArray.h"
@@ -100,12 +101,39 @@ public:
void setIsOval(bool isOval) { fPathRef->setIsOval(isOval); }
+ void setIsRRect(bool isRRect) { fPathRef->setIsRRect(isRRect); }
+
void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); }
private:
SkPathRef* fPathRef;
};
+ class SK_API Iter {
+ public:
+ Iter();
+ Iter(const SkPathRef&);
+
+ void setPathRef(const SkPathRef&);
+
+ /** Return the next verb in this iteration of the path. When all
+ segments have been visited, return kDone_Verb.
+
+ @param pts The points representing the current verb and/or segment
+ This must not be NULL.
+ @return The verb for the current segment
+ */
+ uint8_t next(SkPoint pts[4]);
+
+ SkScalar conicWeight() const { return *fConicWeights; }
+
+ private:
+ const SkPoint* fPts;
+ const uint8_t* fVerbs;
+ const uint8_t* fVerbStop;
+ const SkScalar* fConicWeights;
+ };
+
public:
/**
* Gets a path ref with no verbs or points.
@@ -142,12 +170,20 @@ public:
*/
bool isOval(SkRect* rect) const {
if (fIsOval && rect) {
- *rect = getBounds();
+ *rect = this->getBounds();
}
return SkToBool(fIsOval);
}
+ bool isRRect(SkRRect* rrect) const {
+ if (fIsRRect && rrect) {
+ *rrect = this->getRRect();
+ }
+ return SkToBool(fIsRRect);
+ }
+
+
bool hasComputedBounds() const {
return !fBoundsIsDirty;
}
@@ -164,6 +200,8 @@ public:
return fBounds;
}
+ SkRRect getRRect() const;
+
/**
* Transforms a path ref by a matrix, allocating a new one only if necessary.
*/
@@ -250,6 +288,7 @@ public:
private:
enum SerializationOffsets {
+ kIsRRect_SerializationShift = 26, // requires 1 bit
kIsFinite_SerializationShift = 25, // requires 1 bit
kIsOval_SerializationShift = 24, // requires 1 bit
kSegmentMask_SerializationShift = 0 // requires 4 bits
@@ -265,6 +304,7 @@ private:
fGenerationID = kEmptyGenID;
fSegmentMask = 0;
fIsOval = false;
+ fIsRRect = false;
SkDEBUGCODE(fEditorsAttached = 0;)
SkDEBUGCODE(this->validate();)
}
@@ -312,6 +352,7 @@ private:
fSegmentMask = 0;
fIsOval = false;
+ fIsRRect = false;
size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount;
size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints;
@@ -411,9 +452,18 @@ private:
void setIsOval(bool isOval) { fIsOval = isOval; }
+ void setIsRRect(bool isRRect) { fIsRRect = isRRect; }
+
+ // called only by the editor. Note that this is not a const function.
SkPoint* getPoints() {
SkDEBUGCODE(this->validate();)
fIsOval = false;
+ fIsRRect = false;
+ return fPoints;
+ }
+
+ const SkPoint* getPoints() const {
+ SkDEBUGCODE(this->validate();)
return fPoints;
}
@@ -424,11 +474,6 @@ private:
};
mutable SkRect fBounds;
- mutable uint8_t fBoundsIsDirty;
- mutable SkBool8 fIsFinite; // only meaningful if bounds are valid
-
- SkBool8 fIsOval;
- uint8_t fSegmentMask;
SkPoint* fPoints; // points to begining of the allocation
uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards)
@@ -445,7 +490,15 @@ private:
SkTDArray<GenIDChangeListener*> fGenIDChangeListeners; // pointers are owned
+ mutable uint8_t fBoundsIsDirty;
+ mutable SkBool8 fIsFinite; // only meaningful if bounds are valid
+
+ SkBool8 fIsOval;
+ SkBool8 fIsRRect;
+ uint8_t fSegmentMask;
+
friend class PathRefTest_Private;
+ friend class ForceIsRRect_Private; // unit test isRRect
typedef SkRefCnt INHERITED;
};
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index a3f5e13d7f..2d4976a82a 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1072,6 +1072,7 @@ void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex)
return;
}
+ bool isRRect = hasOnlyMoveTos();
const SkRect& bounds = rrect.getBounds();
if (rrect.isRect()) {
@@ -1119,6 +1120,9 @@ void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex)
}
this->close();
+ SkPathRef::Editor ed(&fPathRef);
+ ed.setIsRRect(isRRect);
+
SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
}
@@ -1861,75 +1865,6 @@ SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
///////////////////////////////////////////////////////////////////////////////
-SkPath::RawIter::RawIter() {
-#ifdef SK_DEBUG
- fPts = nullptr;
- fConicWeights = nullptr;
-#endif
- // need to init enough to make next() harmlessly return kDone_Verb
- fVerbs = nullptr;
- fVerbStop = nullptr;
-}
-
-SkPath::RawIter::RawIter(const SkPath& path) {
- this->setPath(path);
-}
-
-void SkPath::RawIter::setPath(const SkPath& path) {
- fPts = path.fPathRef->points();
- fVerbs = path.fPathRef->verbs();
- fVerbStop = path.fPathRef->verbsMemBegin();
- fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
-}
-
-SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
- SkASSERT(pts);
- if (fVerbs == fVerbStop) {
- return kDone_Verb;
- }
-
- // fVerbs points one beyond next verb so decrement first.
- unsigned verb = *(--fVerbs);
- const SkPoint* srcPts = fPts;
-
- switch (verb) {
- case kMove_Verb:
- pts[0] = srcPts[0];
- srcPts += 1;
- break;
- case kLine_Verb:
- pts[0] = srcPts[-1];
- pts[1] = srcPts[0];
- srcPts += 1;
- break;
- case kConic_Verb:
- fConicWeights += 1;
- // fall-through
- case kQuad_Verb:
- pts[0] = srcPts[-1];
- pts[1] = srcPts[0];
- pts[2] = srcPts[1];
- srcPts += 2;
- break;
- case kCubic_Verb:
- pts[0] = srcPts[-1];
- pts[1] = srcPts[0];
- pts[2] = srcPts[1];
- pts[3] = srcPts[2];
- srcPts += 3;
- break;
- case kClose_Verb:
- break;
- case kDone_Verb:
- SkASSERT(fVerbs == fVerbStop);
- break;
- }
- fPts = srcPts;
- return (Verb)verb;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
/*
Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
*/
diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp
index 119711381f..12429aecfc 100644
--- a/src/core/SkPathRef.cpp
+++ b/src/core/SkPathRef.cpp
@@ -113,15 +113,15 @@ void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst,
(*dst)->fSegmentMask = src.fSegmentMask;
// It's an oval only if it stays a rect.
- (*dst)->fIsOval = src.fIsOval && matrix.rectStaysRect();
+ bool rectStaysRect = matrix.rectStaysRect();
+ (*dst)->fIsOval = src.fIsOval && rectStaysRect;
+ (*dst)->fIsRRect = src.fIsRRect && rectStaysRect;
SkDEBUGCODE((*dst)->validate();)
}
SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) {
SkPathRef* ref = new SkPathRef;
- bool isOval;
- uint8_t segmentMask;
int32_t packed;
if (!buffer->readS32(&packed)) {
@@ -130,8 +130,9 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) {
}
ref->fIsFinite = (packed >> kIsFinite_SerializationShift) & 1;
- segmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF;
- isOval = (packed >> kIsOval_SerializationShift) & 1;
+ uint8_t segmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF;
+ bool isOval = (packed >> kIsOval_SerializationShift) & 1;
+ bool isRRect = (packed >> kIsRRect_SerializationShift) & 1;
int32_t verbCount, pointCount, conicCount;
if (!buffer->readU32(&(ref->fGenerationID)) ||
@@ -159,6 +160,7 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) {
// resetToSize clears fSegmentMask and fIsOval
ref->fSegmentMask = segmentMask;
ref->fIsOval = isOval;
+ ref->fIsRRect = isRRect;
return ref;
}
@@ -174,6 +176,7 @@ void SkPathRef::Rewind(SkAutoTUnref<SkPathRef>* pathRef) {
(*pathRef)->fConicWeights.rewind();
(*pathRef)->fSegmentMask = 0;
(*pathRef)->fIsOval = false;
+ (*pathRef)->fIsRRect = false;
SkDEBUGCODE((*pathRef)->validate();)
} else {
int oldVCnt = (*pathRef)->countVerbs();
@@ -240,6 +243,7 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) const {
int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) |
((fIsOval & 1) << kIsOval_SerializationShift) |
+ ((fIsRRect & 1) << kIsRRect_SerializationShift) |
(fSegmentMask << kSegmentMask_SerializationShift);
buffer->write32(packed);
@@ -281,6 +285,7 @@ void SkPathRef::copy(const SkPathRef& ref,
}
fSegmentMask = ref.fSegmentMask;
fIsOval = ref.fIsOval;
+ fIsRRect = ref.fIsRRect;
SkDEBUGCODE(this->validate();)
}
@@ -352,6 +357,7 @@ SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
fBoundsIsDirty = true; // this also invalidates fIsFinite
if (dirtyAfterEdit) {
fIsOval = false;
+ fIsRRect = false;
}
if (SkPath::kConic_Verb == verb) {
@@ -410,6 +416,7 @@ SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) {
fBoundsIsDirty = true; // this also invalidates fIsFinite
if (dirtyAfterEdit) {
fIsOval = false;
+ fIsRRect = false;
}
if (SkPath::kConic_Verb == verb) {
@@ -456,6 +463,116 @@ void SkPathRef::callGenIDChangeListeners() {
fGenIDChangeListeners.deleteAll();
}
+SkRRect SkPathRef::getRRect() const {
+ const SkRect& bounds = this->getBounds();
+ SkVector radii[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
+ Iter iter(*this);
+ SkPoint pts[4];
+ uint8_t verb = iter.next(pts);
+ SkASSERT(SkPath::kMove_Verb == verb);
+ while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+ if (SkPath::kConic_Verb == verb) {
+ SkVector v1_0 = pts[1] - pts[0];
+ SkVector v2_1 = pts[2] - pts[1];
+ SkVector dxdy;
+ if (v1_0.fX) {
+ SkASSERT(!v2_1.fX && !v1_0.fY);
+ dxdy.set(SkScalarAbs(v1_0.fX), SkScalarAbs(v2_1.fY));
+ } else if (!v1_0.fY) {
+ SkASSERT(!v2_1.fX || !v2_1.fY);
+ dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v2_1.fY));
+ } else {
+ SkASSERT(!v2_1.fY);
+ dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v1_0.fY));
+ }
+ SkRRect::Corner corner =
+ pts[1].fX == bounds.fLeft ?
+ pts[1].fY == bounds.fTop ?
+ SkRRect::kUpperLeft_Corner : SkRRect::kLowerLeft_Corner :
+ pts[1].fY == bounds.fTop ?
+ SkRRect::kUpperRight_Corner : SkRRect::kLowerRight_Corner;
+ SkASSERT(!radii[corner].fX && !radii[corner].fY);
+ radii[corner] = dxdy;
+ } else {
+ SkASSERT((verb == SkPath::kLine_Verb
+ && (!(pts[1].fX - pts[0].fX) || !(pts[1].fY - pts[0].fY)))
+ || verb == SkPath::kClose_Verb);
+ }
+ }
+ SkRRect rrect;
+ rrect.setRectRadii(bounds, radii);
+ return rrect;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPathRef::Iter::Iter() {
+#ifdef SK_DEBUG
+ fPts = nullptr;
+ fConicWeights = nullptr;
+#endif
+ // need to init enough to make next() harmlessly return kDone_Verb
+ fVerbs = nullptr;
+ fVerbStop = nullptr;
+}
+
+SkPathRef::Iter::Iter(const SkPathRef& path) {
+ this->setPathRef(path);
+}
+
+void SkPathRef::Iter::setPathRef(const SkPathRef& path) {
+ fPts = path.points();
+ fVerbs = path.verbs();
+ fVerbStop = path.verbsMemBegin();
+ fConicWeights = path.conicWeights() - 1; // begin one behind
+}
+
+uint8_t SkPathRef::Iter::next(SkPoint pts[4]) {
+ SkASSERT(pts);
+ if (fVerbs == fVerbStop) {
+ return (uint8_t) SkPath::kDone_Verb;
+ }
+
+ // fVerbs points one beyond next verb so decrement first.
+ unsigned verb = *(--fVerbs);
+ const SkPoint* srcPts = fPts;
+
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ pts[0] = srcPts[0];
+ srcPts += 1;
+ break;
+ case SkPath::kLine_Verb:
+ pts[0] = srcPts[-1];
+ pts[1] = srcPts[0];
+ srcPts += 1;
+ break;
+ case SkPath::kConic_Verb:
+ fConicWeights += 1;
+ // fall-through
+ case SkPath::kQuad_Verb:
+ pts[0] = srcPts[-1];
+ pts[1] = srcPts[0];
+ pts[2] = srcPts[1];
+ srcPts += 2;
+ break;
+ case SkPath::kCubic_Verb:
+ pts[0] = srcPts[-1];
+ pts[1] = srcPts[0];
+ pts[2] = srcPts[1];
+ pts[3] = srcPts[2];
+ srcPts += 3;
+ break;
+ case SkPath::kClose_Verb:
+ break;
+ case SkPath::kDone_Verb:
+ SkASSERT(fVerbs == fVerbStop);
+ break;
+ }
+ fPts = srcPts;
+ return (uint8_t) verb;
+}
+
#ifdef SK_DEBUG
void SkPathRef::validate() const {
this->INHERITED::validate();
diff --git a/tests/RRectInPathTest.cpp b/tests/RRectInPathTest.cpp
new file mode 100644
index 0000000000..c6b1d9c15e
--- /dev/null
+++ b/tests/RRectInPathTest.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkPathRef.h"
+#include "SkPathOps.h"
+#include "SkRRect.h"
+#include "Test.h"
+
+static SkRRect path_contains_rrect(skiatest::Reporter* reporter, const SkPath& path) {
+ SkRRect out;
+ REPORTER_ASSERT(reporter, path.isRRect(&out));
+ SkPath path2, xorBoth;
+ path2.addRRect(out);
+ if (path == path2) {
+ return out;
+ }
+ Op(path, path2, SkPathOp::kXOR_SkPathOp, &xorBoth);
+ REPORTER_ASSERT(reporter, xorBoth.isEmpty());
+ return out;
+}
+
+static SkRRect inner_path_contains_rrect(skiatest::Reporter* reporter, const SkRRect& in) {
+ switch (in.getType()) {
+ case SkRRect::kEmpty_Type:
+ case SkRRect::kRect_Type:
+ case SkRRect::kOval_Type:
+ return in;
+ default:
+ break;
+ }
+ SkPath path;
+ path.addRRect(in);
+ return path_contains_rrect(reporter, path);
+}
+
+static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRRect& in) {
+ SkRRect out = inner_path_contains_rrect(reporter, in);
+ if (in != out) {
+ SkDebugf("");
+ }
+ REPORTER_ASSERT(reporter, in == out);
+}
+
+static void path_contains_rrect_nocheck(skiatest::Reporter* reporter, const SkRRect& in) {
+ SkRRect out = inner_path_contains_rrect(reporter, in);
+ if (in == out) {
+ SkDebugf("");
+ }
+}
+
+static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRect& r,
+ SkVector v[4]) {
+ SkRRect rrect;
+ rrect.setRectRadii(r, v);
+ path_contains_rrect_check(reporter, rrect);
+}
+
+class ForceIsRRect_Private {
+public:
+ ForceIsRRect_Private(SkPath* path) {
+ path->fPathRef->setIsRRect(true);
+ }
+};
+
+static void force_path_contains_rrect(skiatest::Reporter* reporter, SkPath& path) {
+ ForceIsRRect_Private force_rrect(&path);
+ path_contains_rrect(reporter, path);
+}
+
+static void test_undetected_paths(skiatest::Reporter* reporter) {
+ SkPath path;
+ path.moveTo(0, 62.5f);
+ path.lineTo(0, 3.5f);
+ path.conicTo(0, 0, 3.5f, 0, 0.70710677f);
+ path.lineTo(196.5f, 0);
+ path.conicTo(200, 0, 200, 3.5f, 0.70710677f);
+ path.lineTo(200, 62.5f);
+ path.conicTo(200, 66, 196.5f, 66, 0.70710677f);
+ path.lineTo(3.5f, 66);
+ path.conicTo(0, 66, 0, 62.5, 0.70710677f);
+ path.close();
+ force_path_contains_rrect(reporter, path);
+
+ path.reset();
+ path.moveTo(0, 81.5f);
+ path.lineTo(0, 3.5f);
+ path.conicTo(0, 0, 3.5f, 0, 0.70710677f);
+ path.lineTo(149.5, 0);
+ path.conicTo(153, 0, 153, 3.5f, 0.70710677f);
+ path.lineTo(153, 81.5f);
+ path.conicTo(153, 85, 149.5f, 85, 0.70710677f);
+ path.lineTo(3.5f, 85);
+ path.conicTo(0, 85, 0, 81.5f, 0.70710677f);
+ path.close();
+ force_path_contains_rrect(reporter, path);
+
+ path.reset();
+ path.moveTo(14, 1189);
+ path.lineTo(14, 21);
+ path.conicTo(14, 14, 21, 14, 0.70710677f);
+ path.lineTo(1363, 14);
+ path.conicTo(1370, 14, 1370, 21, 0.70710677f);
+ path.lineTo(1370, 1189);
+ path.conicTo(1370, 1196, 1363, 1196, 0.70710677f);
+ path.lineTo(21, 1196);
+ path.conicTo(14, 1196, 14, 1189, 0.70710677f);
+ path.close();
+ force_path_contains_rrect(reporter, path);
+
+ path.reset();
+ path.moveTo(14, 1743);
+ path.lineTo(14, 21);
+ path.conicTo(14, 14, 21, 14, 0.70710677f);
+ path.lineTo(1363, 14);
+ path.conicTo(1370, 14, 1370, 21, 0.70710677f);
+ path.lineTo(1370, 1743);
+ path.conicTo(1370, 1750, 1363, 1750, 0.70710677f);
+ path.lineTo(21, 1750);
+ path.conicTo(14, 1750, 14, 1743, 0.70710677f);
+ path.close();
+ force_path_contains_rrect(reporter, path);
+}
+
+static const SkScalar kWidth = 100.0f;
+static const SkScalar kHeight = 100.0f;
+
+static void test_tricky_radii(skiatest::Reporter* reporter) {
+ {
+ // crbug.com/458522
+ SkRRect rr;
+ const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
+ const SkScalar rad = 12814;
+ const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
+ rr.setRectRadii(bounds, vec);
+ path_contains_rrect_check(reporter, rr);
+ }
+
+ {
+ // crbug.com//463920
+ SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0);
+ SkVector radii[4] = {
+ { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f }
+ };
+ SkRRect rr;
+ rr.setRectRadii(r, radii);
+ path_contains_rrect_nocheck(reporter, rr);
+ }
+}
+
+static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
+ SkRRect rr;
+ const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
+ const SkScalar rad = 40;
+ rr.setRectXY(bounds, rad, rad);
+ path_contains_rrect_check(reporter, rr);
+
+ SkRRect other;
+ SkMatrix matrix;
+ matrix.setScale(0, 1);
+ rr.transform(matrix, &other);
+ path_contains_rrect_check(reporter, rr);
+}
+
+static void test_inset(skiatest::Reporter* reporter) {
+ SkRRect rr, rr2;
+ SkRect r = { 0, 0, 100, 100 };
+
+ rr.setRect(r);
+ rr.inset(-20, -20, &rr2);
+ path_contains_rrect_check(reporter, rr);
+
+ rr.inset(20, 20, &rr2);
+ path_contains_rrect_check(reporter, rr);
+
+ rr.inset(r.width()/2, r.height()/2, &rr2);
+ path_contains_rrect_check(reporter, rr);
+
+ rr.setRectXY(r, 20, 20);
+ rr.inset(19, 19, &rr2);
+ path_contains_rrect_check(reporter, rr);
+ rr.inset(20, 20, &rr2);
+ path_contains_rrect_check(reporter, rr);
+}
+
+
+static void test_9patch_rrect(skiatest::Reporter* reporter,
+ const SkRect& rect,
+ SkScalar l, SkScalar t, SkScalar r, SkScalar b,
+ bool checkRadii) {
+ SkRRect rr;
+ rr.setNinePatch(rect, l, t, r, b);
+ if (checkRadii) {
+ path_contains_rrect_check(reporter, rr);
+ } else {
+ path_contains_rrect_nocheck(reporter, rr);
+ }
+
+ SkRRect rr2; // construct the same RR using the most general set function
+ SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } };
+ rr2.setRectRadii(rect, radii);
+ if (checkRadii) {
+ path_contains_rrect_check(reporter, rr);
+ } else {
+ path_contains_rrect_nocheck(reporter, rr);
+ }
+}
+
+// Test out the basic API entry points
+static void test_round_rect_basic(skiatest::Reporter* reporter) {
+
+ //----
+ SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+
+ SkRRect rr1;
+ rr1.setRect(rect);
+ path_contains_rrect_check(reporter, rr1);
+
+ SkRRect rr1_2; // construct the same RR using the most general set function
+ SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
+ rr1_2.setRectRadii(rect, rr1_2_radii);
+ path_contains_rrect_check(reporter, rr1_2);
+ SkRRect rr1_3; // construct the same RR using the nine patch set function
+ rr1_3.setNinePatch(rect, 0, 0, 0, 0);
+ path_contains_rrect_check(reporter, rr1_2);
+
+ //----
+ SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
+ SkRRect rr2;
+ rr2.setOval(rect);
+ path_contains_rrect_check(reporter, rr2);
+
+ SkRRect rr2_2; // construct the same RR using the most general set function
+ SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
+ { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
+ rr2_2.setRectRadii(rect, rr2_2_radii);
+ path_contains_rrect_check(reporter, rr2_2);
+ SkRRect rr2_3; // construct the same RR using the nine patch set function
+ rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
+ path_contains_rrect_check(reporter, rr2_3);
+
+ //----
+ SkPoint p = { 5, 5 };
+ SkRRect rr3;
+ rr3.setRectXY(rect, p.fX, p.fY);
+ path_contains_rrect_check(reporter, rr3);
+
+ SkRRect rr3_2; // construct the same RR using the most general set function
+ SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
+ rr3_2.setRectRadii(rect, rr3_2_radii);
+ path_contains_rrect_check(reporter, rr3_2);
+ SkRRect rr3_3; // construct the same RR using the nine patch set function
+ rr3_3.setNinePatch(rect, 5, 5, 5, 5);
+ path_contains_rrect_check(reporter, rr3_3);
+
+ //----
+ test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
+
+ {
+ // Test out the rrect from skia:3466
+ SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
+
+ test_9patch_rrect(reporter,
+ rect2,
+ 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
+ false);
+ }
+
+ //----
+ SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
+
+ SkRRect rr5;
+ rr5.setRectRadii(rect, radii2);
+ path_contains_rrect_check(reporter, rr5);
+}
+
+// Test out the cases when the RR degenerates to a rect
+static void test_round_rect_rects(skiatest::Reporter* reporter) {
+
+ //----
+ SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+ SkRRect rr1;
+ rr1.setRectXY(rect, 0, 0);
+
+ path_contains_rrect_check(reporter, rr1);
+
+ //----
+ SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
+
+ SkRRect rr2;
+ rr2.setRectRadii(rect, radii);
+
+ path_contains_rrect_check(reporter, rr2);
+
+ //----
+ SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
+
+ SkRRect rr3;
+ rr3.setRectRadii(rect, radii2);
+ path_contains_rrect_check(reporter, rr3);
+}
+
+// Test out the cases when the RR degenerates to an oval
+static void test_round_rect_ovals(skiatest::Reporter* reporter) {
+ //----
+ SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+ SkRRect rr1;
+ rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
+
+ path_contains_rrect_check(reporter, rr1);
+}
+
+// Test out the non-degenerate RR cases
+static void test_round_rect_general(skiatest::Reporter* reporter) {
+ //----
+ SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+ SkRRect rr1;
+ rr1.setRectXY(rect, 20, 20);
+
+ path_contains_rrect_check(reporter, rr1);
+
+ //----
+ SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
+
+ SkRRect rr2;
+ rr2.setRectRadii(rect, radii);
+
+ path_contains_rrect_check(reporter, rr2);
+}
+
+static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
+ SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+ SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
+ SkRRect rr1;
+ rr1.setRectRadii(rect, radii);
+ path_contains_rrect_nocheck(reporter, rr1);
+}
+
+static void set_radii(SkVector radii[4], int index, float rad) {
+ sk_bzero(radii, sizeof(SkVector) * 4);
+ radii[index].set(rad, rad);
+}
+
+static void test_skbug_3239(skiatest::Reporter* reporter) {
+ const float min = SkBits2Float(0xcb7f16c8); /* -16717512.000000 */
+ const float max = SkBits2Float(0x4b7f1c1d); /* 16718877.000000 */
+ const float big = SkBits2Float(0x4b7f1bd7); /* 16718807.000000 */
+
+ const float rad = 33436320;
+
+ const SkRect rectx = SkRect::MakeLTRB(min, min, max, big);
+ const SkRect recty = SkRect::MakeLTRB(min, min, big, max);
+
+ SkVector radii[4];
+ for (int i = 0; i < 4; ++i) {
+ set_radii(radii, i, rad);
+ path_contains_rrect_check(reporter, rectx, radii);
+ path_contains_rrect_check(reporter, recty, radii);
+ }
+}
+
+static void test_mix(skiatest::Reporter* reporter) {
+ // Test out mixed degenerate and non-degenerate geometry with Conics
+ const SkVector radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 100, 100 } };
+ SkRect r = SkRect::MakeWH(100, 100);
+ SkRRect rr;
+ rr.setRectRadii(r, radii);
+ path_contains_rrect_check(reporter, rr);
+}
+
+DEF_TEST(RoundRectInPath, reporter) {
+ test_tricky_radii(reporter);
+ test_empty_crbug_458524(reporter);
+ test_inset(reporter);
+ test_round_rect_basic(reporter);
+ test_round_rect_rects(reporter);
+ test_round_rect_ovals(reporter);
+ test_round_rect_general(reporter);
+ test_undetected_paths(reporter);
+ test_round_rect_iffy_parameters(reporter);
+ test_skbug_3239(reporter);
+ test_mix(reporter);
+}