Update ComputeBlurredRRectParams to compute all the parameters needed for occluded blurred rrect ninepatch draws

This is split out of: https://codereview.chromium.org/2245653002/ (Start using vertex attributes for nine-patch blurred rrect draws)

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2248533002

Review-Url: https://codereview.chromium.org/2248533002
diff --git a/include/effects/SkBlurMaskFilter.h b/include/effects/SkBlurMaskFilter.h
index 12eec60..dfbae6b 100644
--- a/include/effects/SkBlurMaskFilter.h
+++ b/include/effects/SkBlurMaskFilter.h
@@ -73,15 +73,27 @@
                                       SkScalar blurRadius);
 #endif
 
-    static bool ComputeBlurredRRectParams(const SkRRect& rrect,
-                                          SkScalar sigma,
+    static const int kMaxDivisions = 6;
+
+    // This method computes all the parameters for drawing a partially occluded nine-patched
+    // blurred rrect mask:
+    //   rrectToDraw - the integerized rrect to draw in the mask
+    //   widthHeight - how large to make the mask (rrectToDraw will be centered in this coord sys)
+    //   rectXs, rectYs - the x & y coordinates of the covering geometry lattice
+    //   texXs, texYs - the texture coordinate at each point in rectXs & rectYs
+    //   numXs, numYs - number of coordinates in the x & y directions
+    //   skipMask - bit mask that contains a 1-bit whenever one of the cells is occluded
+    // It returns true if 'devRRect' is nine-patchable
+    static bool ComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect,
+                                          const SkRect& occluder,
+                                          SkScalar sigma, SkScalar xformedSigma,
                                           SkRRect* rrectToDraw,
                                           SkISize* widthHeight,
-                                          SkScalar xs[4],
-                                          int* numXs,
-                                          SkScalar ys[4],
-                                          int* numYs);
-
+                                          SkScalar rectXs[kMaxDivisions],
+                                          SkScalar rectYs[kMaxDivisions],
+                                          SkScalar texXs[kMaxDivisions],
+                                          SkScalar texYs[kMaxDivisions],
+                                          int* numXs, int* numYs, uint32_t* skipMask);
 
     SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index 0e6de9e..c044333 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -139,59 +139,144 @@
     return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, occluder, flags));
 }
 
-bool SkBlurMaskFilter::ComputeBlurredRRectParams(const SkRRect& rrect,
-                                                 SkScalar sigma,
+// linearly interpolate between y1 & y3 to match x2's position between x1 & x3
+static SkScalar interp(SkScalar x1, SkScalar x2, SkScalar x3, SkScalar y1, SkScalar y3) {
+    SkASSERT(x1 <= x2 && x2 <= x3);
+    SkASSERT(y1 <= y3);
+
+    SkScalar t = (x2 - x1) / (x3 - x1);
+    return y1 + t * (y3 - y1);
+}
+
+// Insert 'lower' and 'higher' into 'array1' and insert a new value at each matching insertion
+// point in 'array2' that linearly interpolates between the existing values.
+// Return a bit mask which contains a copy of 'inputMask' for all the cells between the two
+// insertion points.
+static uint32_t insert_into_arrays(SkScalar* array1, SkScalar* array2,
+                                   SkScalar lower, SkScalar higher,
+                                   int* num, uint32_t inputMask, int maskSize) {
+    SkASSERT(lower < higher);
+    SkASSERT(lower >= array1[0] && higher <= array1[*num-1]);
+
+    int32_t skipMask = 0x0;
+    int i;
+    for (i = 0; i < *num; ++i) {
+        if (lower >= array1[i] && lower < array1[i+1]) {
+            if (!SkScalarNearlyEqual(lower, array1[i])) {
+                memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
+                array1[i+1] = lower;
+                memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
+                array2[i+1] = interp(array1[i], lower, array1[i+2], array2[i], array2[i+2]);
+                i++;
+                (*num)++;
+            }
+            break;
+        }
+    }
+    for ( ; i < *num; ++i) {
+        skipMask |= inputMask << (i*maskSize);
+        if (higher > array1[i] && higher <= array1[i+1]) {
+            if (!SkScalarNearlyEqual(higher, array1[i+1])) {
+                memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
+                array1[i+1] = higher;
+                memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
+                array2[i+1] = interp(array1[i], higher, array1[i+2], array2[i], array2[i+2]);
+                (*num)++;
+            }
+            break;
+        } 
+    }
+
+    return skipMask;
+}
+
+bool SkBlurMaskFilter::ComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect,
+                                                 const SkRect& occluder,
+                                                 SkScalar sigma, SkScalar xformedSigma,
                                                  SkRRect* rrectToDraw,
                                                  SkISize* widthHeight,
-                                                 SkScalar xs[4],
-                                                 int* numXs,
-                                                 SkScalar ys[4],
-                                                 int* numYs) {
-    unsigned int blurRadius = 3*SkScalarCeilToInt(sigma-1/6.0f);
+                                                 SkScalar rectXs[kMaxDivisions],
+                                                 SkScalar rectYs[kMaxDivisions],
+                                                 SkScalar texXs[kMaxDivisions],
+                                                 SkScalar texYs[kMaxDivisions],
+                                                 int* numXs, int* numYs, uint32_t* skipMask) {
+    unsigned int devBlurRadius = 3*SkScalarCeilToInt(xformedSigma-1/6.0f);
+    SkScalar srcBlurRadius = 3.0f * sigma;
 
-    const SkRect& orig = rrect.getBounds();
-    const SkVector& radiiUL = rrect.radii(SkRRect::kUpperLeft_Corner);
-    const SkVector& radiiUR = rrect.radii(SkRRect::kUpperRight_Corner);
-    const SkVector& radiiLR = rrect.radii(SkRRect::kLowerRight_Corner);
-    const SkVector& radiiLL = rrect.radii(SkRRect::kLowerLeft_Corner);
+    const SkRect& devOrig = devRRect.getBounds();
+    const SkVector& devRadiiUL = devRRect.radii(SkRRect::kUpperLeft_Corner);
+    const SkVector& devRadiiUR = devRRect.radii(SkRRect::kUpperRight_Corner);
+    const SkVector& devRadiiLR = devRRect.radii(SkRRect::kLowerRight_Corner);
+    const SkVector& devRadiiLL = devRRect.radii(SkRRect::kLowerLeft_Corner);
 
-    const int left  = SkScalarCeilToInt(SkTMax<SkScalar>(radiiUL.fX, radiiLL.fX));
-    const int top   = SkScalarCeilToInt(SkTMax<SkScalar>(radiiUL.fY, radiiUR.fY));
-    const int right = SkScalarCeilToInt(SkTMax<SkScalar>(radiiUR.fX, radiiLR.fX));
-    const int bot   = SkScalarCeilToInt(SkTMax<SkScalar>(radiiLL.fY, radiiLR.fY));
+    const int devLeft  = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fX, devRadiiLL.fX));
+    const int devTop   = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fY, devRadiiUR.fY));
+    const int devRight = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUR.fX, devRadiiLR.fX));
+    const int devBot   = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiLL.fY, devRadiiLR.fY));
 
     // This is a conservative check for nine-patchability
-    if (orig.fLeft + left + blurRadius >= orig.fRight  - right - blurRadius || 
-        orig.fTop  + top  + blurRadius >= orig.fBottom - bot   - blurRadius) {
+    if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight  - devRight - devBlurRadius ||
+        devOrig.fTop  + devTop  + devBlurRadius >= devOrig.fBottom - devBot   - devBlurRadius) {
         return false;
     }
 
-    int newRRWidth, newRRHeight;
+    const SkVector& srcRadiiUL = srcRRect.radii(SkRRect::kUpperLeft_Corner);
+    const SkVector& srcRadiiUR = srcRRect.radii(SkRRect::kUpperRight_Corner);
+    const SkVector& srcRadiiLR = srcRRect.radii(SkRRect::kLowerRight_Corner);
+    const SkVector& srcRadiiLL = srcRRect.radii(SkRRect::kLowerLeft_Corner);
 
-    // 3x3 case
-    newRRWidth = 2*blurRadius + left + right + 1;
-    newRRHeight = 2*blurRadius + top + bot + 1;
-    widthHeight->fWidth = newRRWidth + 2 * blurRadius;
-    widthHeight->fHeight = newRRHeight + 2 * blurRadius;
-    // TODO: need to return non-normalized indices 
-    xs[0] = 0.0f;
-    xs[1] = (blurRadius + left) / (float) widthHeight->fWidth;
-    xs[2] = (blurRadius + left + 1.0f) / widthHeight->fWidth;
-    xs[3] = 1.0f;
+    const SkScalar srcLeft  = SkTMax<SkScalar>(srcRadiiUL.fX, srcRadiiLL.fX);
+    const SkScalar srcTop   = SkTMax<SkScalar>(srcRadiiUL.fY, srcRadiiUR.fY);
+    const SkScalar srcRight = SkTMax<SkScalar>(srcRadiiUR.fX, srcRadiiLR.fX);
+    const SkScalar srcBot   = SkTMax<SkScalar>(srcRadiiLL.fY, srcRadiiLR.fY);
+
+    int newRRWidth = 2*devBlurRadius + devLeft + devRight + 1;
+    int newRRHeight = 2*devBlurRadius + devTop + devBot + 1;
+    widthHeight->fWidth = newRRWidth + 2 * devBlurRadius;
+    widthHeight->fHeight = newRRHeight + 2 * devBlurRadius;
+
+    const SkRect srcProxyRect = srcRRect.getBounds().makeOutset(srcBlurRadius, srcBlurRadius);
+
+    rectXs[0] = srcProxyRect.fLeft;
+    rectXs[1] = srcProxyRect.fLeft + 2*srcBlurRadius + srcLeft;
+    rectXs[2] = srcProxyRect.fRight - 2*srcBlurRadius - srcRight;
+    rectXs[3] = srcProxyRect.fRight;
+
+    rectYs[0] = srcProxyRect.fTop;
+    rectYs[1] = srcProxyRect.fTop + 2*srcBlurRadius + srcTop;
+    rectYs[2] = srcProxyRect.fBottom - 2*srcBlurRadius - srcBot;
+    rectYs[3] = srcProxyRect.fBottom;
+
+    texXs[0] = 0.0f;
+    texXs[1] = 2.0f*devBlurRadius + devLeft;
+    texXs[2] = 2.0f*devBlurRadius + devLeft + 1;
+    texXs[3] = SkIntToScalar(widthHeight->fWidth);
+
+    texYs[0] = 0.0f;
+    texYs[1] = 2.0f*devBlurRadius + devTop;
+    texYs[2] = 2.0f*devBlurRadius + devTop + 1;
+    texYs[3] = SkIntToScalar(widthHeight->fHeight);
+
+    SkRect temp = occluder;
+
     *numXs = 4;
-    ys[0] = 0.0f;
-    ys[1] = (blurRadius + top) / (float) widthHeight->fHeight;
-    ys[2] = (blurRadius + top + 1.0f) / widthHeight->fHeight;
-    ys[3] = 1.0f;
     *numYs = 4;
+    *skipMask = 0;
+    if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) {
+        *skipMask = insert_into_arrays(rectXs, texXs, temp.fLeft, temp.fRight, numXs, 0x1, 1);
+        *skipMask = insert_into_arrays(rectYs, texYs, temp.fTop, temp.fBottom,
+                                       numYs, *skipMask, *numXs-1);
+    }
 
-    const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(blurRadius), SkIntToScalar(blurRadius),
-                                            SkIntToScalar(newRRWidth), SkIntToScalar(newRRHeight));
+    const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius),
+                                            SkIntToScalar(devBlurRadius),
+                                            SkIntToScalar(newRRWidth),
+                                            SkIntToScalar(newRRHeight));
     SkVector newRadii[4];
-    newRadii[0] = { SkScalarCeilToScalar(radiiUL.fX), SkScalarCeilToScalar(radiiUL.fY) };
-    newRadii[1] = { SkScalarCeilToScalar(radiiUR.fX), SkScalarCeilToScalar(radiiUR.fY) };
-    newRadii[2] = { SkScalarCeilToScalar(radiiLR.fX), SkScalarCeilToScalar(radiiLR.fY) };
-    newRadii[3] = { SkScalarCeilToScalar(radiiLL.fX), SkScalarCeilToScalar(radiiLL.fY) };
+    newRadii[0] = { SkScalarCeilToScalar(devRadiiUL.fX), SkScalarCeilToScalar(devRadiiUL.fY) };
+    newRadii[1] = { SkScalarCeilToScalar(devRadiiUR.fX), SkScalarCeilToScalar(devRadiiUR.fY) };
+    newRadii[2] = { SkScalarCeilToScalar(devRadiiLR.fX), SkScalarCeilToScalar(devRadiiLR.fY) };
+    newRadii[3] = { SkScalarCeilToScalar(devRadiiLL.fX), SkScalarCeilToScalar(devRadiiLL.fY) };
 
     rrectToDraw->setRectRadii(newRect, newRadii);
     return true;
@@ -983,7 +1068,9 @@
 class GrRRectBlurEffect : public GrFragmentProcessor {
 public:
 
-    static sk_sp<GrFragmentProcessor> Make(GrContext*, float sigma, const SkRRect&);
+    static sk_sp<GrFragmentProcessor> Make(GrContext*,
+                                           float sigma, float xformedSigma,
+                                           const SkRRect& srcRRect, const SkRRect& devRRect);
 
     virtual ~GrRRectBlurEffect() {};
     const char* name() const override { return "GrRRectBlur"; }
@@ -1069,8 +1156,8 @@
 }
 
 sk_sp<GrFragmentProcessor> GrRRectBlurEffect::Make(GrContext* context,
-                                                   float xformedSigma,
-                                                   const SkRRect& devRRect) {
+                                                   float sigma, float xformedSigma,
+                                                   const SkRRect& srcRRect, const SkRRect& devRRect) {
     SkASSERT(!devRRect.isCircle()); // Should've been caught up-stream
 
     // TODO: loosen this up
@@ -1083,13 +1170,18 @@
     // width (and height) of the rrect.
     SkRRect rrectToDraw;
     SkISize size;
-    SkScalar ignored[4];
+    SkScalar ignored[SkBlurMaskFilter::kMaxDivisions];
     int ignoredSize;
+    uint32_t ignored32;
 
-    bool ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(devRRect, xformedSigma,
+    bool ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(srcRRect, devRRect, 
+                                                                     SkRect::MakeEmpty(),
+                                                                     sigma, xformedSigma,
                                                                      &rrectToDraw, &size,
-                                                                     ignored, &ignoredSize,
-                                                                     ignored, &ignoredSize);
+                                                                     ignored, ignored,
+                                                                     ignored, ignored, 
+                                                                     &ignoredSize, &ignoredSize, 
+                                                                     &ignored32);
     if (!ninePatchable) {
         return nullptr;
     }
@@ -1136,7 +1228,7 @@
     SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
     SkRRect rrect;
     rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
-    return GrRRectBlurEffect::Make(d->fContext, sigma, rrect);
+    return GrRRectBlurEffect::Make(d->fContext, sigma, sigma, rrect, rrect);
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -1279,7 +1371,8 @@
         return true;
     }
 
-    sk_sp<GrFragmentProcessor> fp(GrRRectBlurEffect::Make(context, xformedSigma, devRRect));
+    sk_sp<GrFragmentProcessor> fp(GrRRectBlurEffect::Make(context, fSigma, xformedSigma,
+                                                          srcRRect, devRRect));
     if (!fp) {
         return false;
     }
diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp
index 32e2930..d9fca98 100644
--- a/tests/BlurTest.cpp
+++ b/tests/BlurTest.cpp
@@ -577,12 +577,15 @@
 
 DEF_TEST(BlurredRRectNinePatchComputation, reporter) {
     const SkRect r = SkRect::MakeXYWH(10, 10, 100, 100);
+    static const SkScalar kBlurRad = 3.0f;
 
     bool ninePatchable;
     SkRRect rrectToDraw;
     SkISize size;
-    SkScalar xs[4], ys[4];
-    int numXs, numYs;
+    SkScalar rectXs[SkBlurMaskFilter::kMaxDivisions], rectYs[SkBlurMaskFilter::kMaxDivisions];
+    SkScalar texXs[SkBlurMaskFilter::kMaxDivisions], texYs[SkBlurMaskFilter::kMaxDivisions];
+    int numX, numY;
+    uint32_t skipMask;
 
     // not nine-patchable
     {
@@ -591,47 +594,123 @@
         SkRRect rr;
         rr.setRectRadii(r, radii);
 
-        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, 3.0f, &rrectToDraw, &size,
-                                                                    xs, &numXs, ys, &numYs);   
+        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(),
+                                                                    kBlurRad, kBlurRad,
+                                                                    &rrectToDraw, &size,
+                                                                    rectXs, rectYs, texXs, texYs,
+                                                                    &numX, &numY, &skipMask);   
         REPORTER_ASSERT(reporter, !ninePatchable);
     }
 
     // simple circular
     {
+        static const SkScalar kCornerRad = 10.0f;
         SkRRect rr;
-        rr.setRectXY(r, 10, 10);
+        rr.setRectXY(r, kCornerRad, kCornerRad);
 
-        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, 3.0f, &rrectToDraw, &size,
-                                                                    xs, &numXs, ys, &numYs);   
+        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(),
+                                                                    kBlurRad, kBlurRad,
+                                                                    &rrectToDraw, &size,
+                                                                    rectXs, rectYs, texXs, texYs,
+                                                                    &numX, &numY, &skipMask);
+
+        static const SkScalar kAns = 12.0f * kBlurRad + 2.0f * kCornerRad + 1.0f;
         REPORTER_ASSERT(reporter, ninePatchable);
-        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), 57.0f));
-        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), 57.0));
-        REPORTER_ASSERT(reporter, 4 == numXs && 4 == numYs);
-        for (int i = 0; i < numXs; ++i) {
-            REPORTER_ASSERT(reporter, xs[i] >= 0.0f && xs[i] <= 1.0f);
-        }
-        for (int i = 0; i < numYs; ++i) {
-            REPORTER_ASSERT(reporter, ys[i] >= 0.0f && ys[i] <= 1.0f);
-        }
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kAns));
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kAns));
+        REPORTER_ASSERT(reporter, 4 == numX && 4 == numY);
+        REPORTER_ASSERT(reporter, !skipMask);
     }
 
     // simple elliptical
     {
+        static const SkScalar kXCornerRad = 2.0f;
+        static const SkScalar kYCornerRad = 10.0f;
         SkRRect rr;
-        rr.setRectXY(r, 2, 10);
+        rr.setRectXY(r, kXCornerRad, kYCornerRad);
 
-        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, 3.0f, &rrectToDraw, &size,
-                                                                    xs, &numXs, ys, &numYs);   
+        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(),
+                                                                    kBlurRad, kBlurRad,
+                                                                    &rrectToDraw, &size,
+                                                                    rectXs, rectYs, texXs, texYs,
+                                                                    &numX, &numY, &skipMask);
+
+        static const SkScalar kXAns = 12.0f * kBlurRad + 2.0f * kXCornerRad + 1.0f;
+        static const SkScalar kYAns = 12.0f * kBlurRad + 2.0f * kYCornerRad + 1.0f;
+
         REPORTER_ASSERT(reporter, ninePatchable);
-        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), 41.0f));
-        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), 57.0));
-        REPORTER_ASSERT(reporter, 4 == numXs && 4 == numYs);
-        for (int i = 0; i < numXs; ++i) {
-            REPORTER_ASSERT(reporter, xs[i] >= 0.0f && xs[i] <= 1.0f);
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kXAns));
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kYAns));
+        REPORTER_ASSERT(reporter, 4 == numX && 4 == numY);
+        REPORTER_ASSERT(reporter, !skipMask);
+    }
+
+    // test-out occlusion
+    {
+        static const SkScalar kCornerRad = 10.0f;
+        SkRRect rr;
+        rr.setRectXY(r, kCornerRad, kCornerRad);
+
+        // The rectXs & rectYs should be { 1, 29, 91, 119 }. Add two more points around each.
+        SkScalar testLocs[] = {
+             -18.0f, -9.0f,
+               1.0f,
+               9.0f, 18.0f, 
+              29.0f, 
+              39.0f, 49.0f,
+              91.0f,
+             109.0f, 118.0f,
+             119.0f,
+             139.0f, 149.0f
+        };
+
+        for (int minY = 0; minY < (int)SK_ARRAY_COUNT(testLocs); ++minY) {
+            for (int maxY = minY+1; maxY < (int)SK_ARRAY_COUNT(testLocs); ++maxY) {
+                for (int minX = 0; minX < (int)SK_ARRAY_COUNT(testLocs); ++minX) {
+                    for (int maxX = minX+1; maxX < (int)SK_ARRAY_COUNT(testLocs); ++maxX) {
+                        SkRect occluder = SkRect::MakeLTRB(testLocs[minX], testLocs[minY],
+                                                           testLocs[maxX], testLocs[maxY]);
+                        if (occluder.isEmpty()) {
+                            continue;
+                        }
+
+                        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(
+                                                                    rr, rr, occluder,
+                                                                    kBlurRad, kBlurRad,
+                                                                    &rrectToDraw, &size,
+                                                                    rectXs, rectYs, texXs, texYs,
+                                                                    &numX, &numY, &skipMask);     
+
+                        static const SkScalar kAns = 12.0f * kBlurRad + 2.0f * kCornerRad + 1.0f;
+                        REPORTER_ASSERT(reporter, ninePatchable);
+                        REPORTER_ASSERT(reporter,
+                                            SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kAns));
+                        REPORTER_ASSERT(reporter,
+                                            SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kAns));
+
+                        int checkBit = 0x1;
+                        for (int y = 0; y < numY-1; ++y) {
+                            for (int x = 0; x < numX-1; ++x) {
+                                SkRect cell = SkRect::MakeLTRB(rectXs[x], rectYs[y],
+                                                               rectXs[x+1], rectYs[y+1]);
+                                REPORTER_ASSERT(reporter,
+                                                    SkToBool(skipMask & checkBit) ==
+                                                    (cell.isEmpty() || occluder.contains(cell)));
+
+                                REPORTER_ASSERT(reporter, texXs[x] >= 0 &&
+                                                          texXs[x] <= size.fWidth);
+                                REPORTER_ASSERT(reporter, texYs[y] >= 0 &&
+                                                          texXs[y] <= size.fHeight);
+
+                                checkBit <<= 1;
+                            }
+                        }
+                    }
+                }
+            }
         }
-        for (int i = 0; i < numYs; ++i) {
-            REPORTER_ASSERT(reporter, ys[i] >= 0.0f && ys[i] <= 1.0f);
-        }
+
+
     }
 
 }