allow conic chop to fail

Fuzzy values may cause the conic chop to fail.

Check to see if the values are all finite, and
require the caller to do the same.

R=reed@google.com
BUG=650178
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2368993002

Review-Url: https://codereview.chromium.org/2368993002
This commit is contained in:
caryclark 2016-09-26 11:03:54 -07:00 committed by Commit bot
parent 849f5027e9
commit 414c4295f9
9 changed files with 70 additions and 19 deletions

View File

@ -353,7 +353,10 @@ private:
if (dst) {
SkConic conic;
conic.set(src, weight);
conic.chopAt(t, dst);
if (!conic.chopAt(t, dst)) {
dst[0].set(src, weight);
return 1;
}
}
return 2;
}

View File

@ -220,7 +220,9 @@ static void add_path_segment(int index, SkPath* path) {
SkConic chop[2];
SkConic conic;
conic.set(pts, iter.conicWeight());
conic.chopAt(0.5f, chop);
if (!conic.chopAt(0.5f, chop)) {
return;
}
result.conicTo(chop[0].fPts[1], chop[0].fPts[2], chop[0].fW);
pts[1] = chop[1].fPts[1];
weight = chop[1].fW;
@ -1360,9 +1362,10 @@ public:
SkConic split[2];
SkConic conic;
conic.set(pts, weight);
conic.chopAt(0.5f, split);
conic_coverage(split[0].fPts, split[0].fW, distanceMap, w, h);
conic_coverage(split[1].fPts, split[1].fW, distanceMap, w, h);
if (conic.chopAt(0.5f, split)) {
conic_coverage(split[0].fPts, split[0].fW, distanceMap, w, h);
conic_coverage(split[1].fPts, split[1].fW, distanceMap, w, h);
}
}
void cubic_coverage(SkPoint pts[4], uint8_t* distanceMap, int w, int h) {

View File

@ -975,7 +975,8 @@ static void ratquad_mapTo3D(const SkPoint src[3], SkScalar w, SkP3D dst[]) {
dst[2].set(src[2].fX * 1, src[2].fY * 1, 1);
}
void SkConic::chopAt(SkScalar t, SkConic dst[2]) const {
// return false if infinity or NaN is generated; caller must check
bool SkConic::chopAt(SkScalar t, SkConic dst[2]) const {
SkP3D tmp[3], tmp2[3];
ratquad_mapTo3D(fPts, fW, tmp);
@ -1001,18 +1002,23 @@ void SkConic::chopAt(SkScalar t, SkConic dst[2]) const {
SkScalar root = SkScalarSqrt(tmp2[1].fZ);
dst[0].fW = tmp2[0].fZ / root;
dst[1].fW = tmp2[2].fZ / root;
SkASSERT(sizeof(dst[0]) == sizeof(SkScalar) * 7);
SkASSERT(0 == offsetof(SkConic, fPts[0].fX));
return SkScalarsAreFinite(&dst[0].fPts[0].fX, 7 * 2);
}
void SkConic::chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const {
if (0 == t1 || 1 == t2) {
if (0 == t1 && 1 == t2) {
*dst = *this;
return;
} else {
SkConic pair[2];
this->chopAt(t1 ? t1 : t2, pair);
*dst = pair[SkToBool(t1)];
if (this->chopAt(t1 ? t1 : t2, pair)) {
*dst = pair[SkToBool(t1)];
return;
}
}
return;
}
SkConicCoeff coeff(*this);
Sk2s tt1(t1);
@ -1243,7 +1249,10 @@ bool SkConic::findYExtrema(SkScalar* t) const {
bool SkConic::chopAtXExtrema(SkConic dst[2]) const {
SkScalar t;
if (this->findXExtrema(&t)) {
this->chopAt(t, dst);
if (!this->chopAt(t, dst)) {
// if chop can't return finite values, don't chop
return false;
}
// now clean-up the middle, since we know t was meant to be at
// an X-extrema
SkScalar value = dst[0].fPts[2].fX;
@ -1258,7 +1267,10 @@ bool SkConic::chopAtXExtrema(SkConic dst[2]) const {
bool SkConic::chopAtYExtrema(SkConic dst[2]) const {
SkScalar t;
if (this->findYExtrema(&t)) {
this->chopAt(t, dst);
if (!this->chopAt(t, dst)) {
// if chop can't return finite values, don't chop
return false;
}
// now clean-up the middle, since we know t was meant to be at
// an Y-extrema
SkScalar value = dst[0].fPts[2].fY;

View File

@ -215,7 +215,7 @@ struct SkConic {
* be used.
*/
void evalAt(SkScalar t, SkPoint* pos, SkVector* tangent = nullptr) const;
void chopAt(SkScalar t, SkConic dst[2]) const;
bool SK_WARN_UNUSED_RESULT chopAt(SkScalar t, SkConic dst[2]) const;
void chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const;
void chop(SkConic dst[2]) const;

View File

@ -85,14 +85,16 @@ void SkPathMeasure_segTo(const SkPoint pts[], unsigned segType,
dst->conicTo(conic.fPts[1], conic.fPts[2], conic.fW);
} else {
SkConic tmp[2];
conic.chopAt(stopT, tmp);
dst->conicTo(tmp[0].fPts[1], tmp[0].fPts[2], tmp[0].fW);
if (conic.chopAt(stopT, tmp)) {
dst->conicTo(tmp[0].fPts[1], tmp[0].fPts[2], tmp[0].fW);
}
}
} else {
if (SK_Scalar1 == stopT) {
SkConic tmp1[2];
conic.chopAt(startT, tmp1);
dst->conicTo(tmp1[1].fPts[1], tmp1[1].fPts[2], tmp1[1].fW);
if (conic.chopAt(startT, tmp1)) {
dst->conicTo(tmp1[1].fPts[1], tmp1[1].fPts[2], tmp1[1].fW);
}
} else {
SkConic tmp;
conic.chopAt(startT, stopT, &tmp);

View File

@ -145,7 +145,10 @@ static int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weig
if (dst) {
SkConic conic;
conic.set(src, weight);
conic.chopAt(t, dst);
if (!conic.chopAt(t, dst)) {
dst[0].set(src, weight);
return 1;
}
}
return 2;
}

View File

@ -231,7 +231,11 @@ bool SkOpEdgeBuilder::walk() {
if (maxCurvature > 0) {
SkConic conic(pointsPtr, weight);
SkConic pair[2];
conic.chopAt(maxCurvature, pair);
if (!conic.chopAt(maxCurvature, pair)) {
// if result can't be computed, use original
fCurrentContour->addConic(pointsPtr, weight);
break;
}
SkPoint cStorage[2][3];
SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]);
SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]);

View File

@ -165,7 +165,9 @@ static void chopBothWays(const SkDConic& dConic, double t, const char* name) {
conic.fW = dConic.fWeight;
SkConic chopped[2];
SkDConic dChopped[2];
conic.chopAt(SkDoubleToScalar(t), chopped);
if (!conic.chopAt(SkDoubleToScalar(t), chopped)) {
return;
}
dChopped[0] = dConic.subDivide(0, t);
dChopped[1] = dConic.subDivide(t, 1);
#if DEBUG_VISUALIZE_CONICS

View File

@ -7915,7 +7915,29 @@ path.cubicTo(SkBits2Float(0x6d06f06a), SkBits2Float(0xe30465cf), SkBits2Float(0x
testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
}
static void fuzz763_44(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType((SkPath::FillType) 1);
path.moveTo(SkBits2Float(0x7c223bab), SkBits2Float(0x7cf35966)); // 3.36945e+36f, 1.01083e+37f
path.quadTo(SkBits2Float(0x00000000), SkBits2Float(0x7ccaca6d), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 8.4236e+36f, 0, 0
path.lineTo(SkBits2Float(0x7d7d7d7d), SkBits2Float(0x00000000)); // 2.10591e+37f, 0
path.quadTo(SkBits2Float(0x7ccacab0), SkBits2Float(0x7d1817f4), SkBits2Float(0x7c223bab), SkBits2Float(0x7cf35966)); // 8.42364e+36f, 1.26354e+37f, 3.36945e+36f, 1.01083e+37f
path.close();
SkPath path1(path);
path.reset();
path.setFillType((SkPath::FillType) 0);
path.moveTo(SkBits2Float(0x109d0000), SkBits2Float(0xff7bc000)); // 6.19256e-29f, -3.34633e+38f
path.conicTo(SkBits2Float(0x979797ed), SkBits2Float(0x3a214797), SkBits2Float(0x28aa217a), SkBits2Float(0x01007272), SkBits2Float(0x00000072)); // -9.7965e-25f, 0.000615233f, 1.88883e-14f, 2.3592e-38f, 1.59748e-43f
path.quadTo(SkBits2Float(0x72728302), SkBits2Float(0x8b727272), SkBits2Float(0x72727272), SkBits2Float(0xc00308f6)); // 4.80344e+30f, -4.66936e-32f, 4.80216e+30f, -2.04742f
path.conicTo(SkBits2Float(0x7f52753a), SkBits2Float(0x8072ffff), SkBits2Float(0x67af2103), SkBits2Float(0x7d2a6847), SkBits2Float(0x7d7d7d7d)); // 2.79747e+38f, -1.05611e-38f, 1.65405e+24f, 1.41569e+37f, 2.10591e+37f
SkPath path2(path);
testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
}
static struct TestDesc failTests[] = {
TEST(fuzz763_44),
TEST(fuzz763_43),
TEST(fuzz763_42),
TEST(fuzz763_41),