Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkBlurMask.h"
      9 #include "SkBlurMaskFilter.h"
     10 #include "SkBlurDrawLooper.h"
     11 #include "SkCanvas.h"
     12 #include "SkColorFilter.h"
     13 #include "SkEmbossMaskFilter.h"
     14 #include "SkLayerDrawLooper.h"
     15 #include "SkMath.h"
     16 #include "SkPaint.h"
     17 #include "SkPath.h"
     18 #include "Test.h"
     19 
     20 #if SK_SUPPORT_GPU
     21 #include "GrContextFactory.h"
     22 #include "SkGpuDevice.h"
     23 #endif
     24 
     25 #define WRITE_CSV 0
     26 
     27 ///////////////////////////////////////////////////////////////////////////////
     28 
     29 #define ILLEGAL_MODE    ((SkXfermode::Mode)-1)
     30 
     31 static const int outset = 100;
     32 static const SkColor bgColor = SK_ColorWHITE;
     33 static const int strokeWidth = 4;
     34 
     35 static void create(SkBitmap* bm, const SkIRect& bound) {
     36     bm->allocN32Pixels(bound.width(), bound.height());
     37 }
     38 
     39 static void drawBG(SkCanvas* canvas) {
     40     canvas->drawColor(bgColor);
     41 }
     42 
     43 
     44 struct BlurTest {
     45     void (*addPath)(SkPath*);
     46     int viewLen;
     47     SkIRect views[9];
     48 };
     49 
     50 //Path Draw Procs
     51 //Beware that paths themselves my draw differently depending on the clip.
     52 static void draw50x50Rect(SkPath* path) {
     53     path->addRect(0, 0, SkIntToScalar(50), SkIntToScalar(50));
     54 }
     55 
     56 //Tests
     57 static BlurTest tests[] = {
     58     { draw50x50Rect, 3, {
     59         //inner half of blur
     60         { 0, 0, 50, 50 },
     61         //blur, but no path.
     62         { 50 + strokeWidth/2, 50 + strokeWidth/2, 100, 100 },
     63         //just an edge
     64         { 40, strokeWidth, 60, 50 - strokeWidth },
     65     }},
     66 };
     67 
     68 /** Assumes that the ref draw was completely inside ref canvas --
     69     implies that everything outside is "bgColor".
     70     Checks that all overlap is the same and that all non-overlap on the
     71     ref is "bgColor".
     72  */
     73 static bool compare(const SkBitmap& ref, const SkIRect& iref,
     74                     const SkBitmap& test, const SkIRect& itest)
     75 {
     76     const int xOff = itest.fLeft - iref.fLeft;
     77     const int yOff = itest.fTop - iref.fTop;
     78 
     79     for (int y = 0; y < test.height(); ++y) {
     80         for (int x = 0; x < test.width(); ++x) {
     81             SkColor testColor = test.getColor(x, y);
     82             int refX = x + xOff;
     83             int refY = y + yOff;
     84             SkColor refColor;
     85             if (refX >= 0 && refX < ref.width() &&
     86                 refY >= 0 && refY < ref.height())
     87             {
     88                 refColor = ref.getColor(refX, refY);
     89             } else {
     90                 refColor = bgColor;
     91             }
     92             if (refColor != testColor) {
     93                 return false;
     94             }
     95         }
     96     }
     97     return true;
     98 }
     99 
    100 DEF_TEST(BlurDrawing, reporter) {
    101     SkPaint paint;
    102     paint.setColor(SK_ColorGRAY);
    103     paint.setStyle(SkPaint::kStroke_Style);
    104     paint.setStrokeWidth(SkIntToScalar(strokeWidth));
    105 
    106     SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5));
    107     for (int style = 0; style <= kLastEnum_SkBlurStyle; ++style) {
    108         SkBlurStyle blurStyle = static_cast<SkBlurStyle>(style);
    109 
    110         const uint32_t flagPermutations = SkBlurMaskFilter::kAll_BlurFlag;
    111         for (uint32_t flags = 0; flags < flagPermutations; ++flags) {
    112             paint.setMaskFilter(SkBlurMaskFilter::Make(blurStyle, sigma, flags));
    113 
    114             for (size_t test = 0; test < SK_ARRAY_COUNT(tests); ++test) {
    115                 SkPath path;
    116                 tests[test].addPath(&path);
    117                 SkPath strokedPath;
    118                 paint.getFillPath(path, &strokedPath);
    119                 SkRect refBound = strokedPath.getBounds();
    120                 SkIRect iref;
    121                 refBound.roundOut(&iref);
    122                 iref.inset(-outset, -outset);
    123                 SkBitmap refBitmap;
    124                 create(&refBitmap, iref);
    125 
    126                 SkCanvas refCanvas(refBitmap);
    127                 refCanvas.translate(SkIntToScalar(-iref.fLeft),
    128                                     SkIntToScalar(-iref.fTop));
    129                 drawBG(&refCanvas);
    130                 refCanvas.drawPath(path, paint);
    131 
    132                 for (int view = 0; view < tests[test].viewLen; ++view) {
    133                     SkIRect itest = tests[test].views[view];
    134                     SkBitmap testBitmap;
    135                     create(&testBitmap, itest);
    136 
    137                     SkCanvas testCanvas(testBitmap);
    138                     testCanvas.translate(SkIntToScalar(-itest.fLeft),
    139                                          SkIntToScalar(-itest.fTop));
    140                     drawBG(&testCanvas);
    141                     testCanvas.drawPath(path, paint);
    142 
    143                     REPORTER_ASSERT(reporter,
    144                         compare(refBitmap, iref, testBitmap, itest));
    145                 }
    146             }
    147         }
    148     }
    149 }
    150 
    151 ///////////////////////////////////////////////////////////////////////////////
    152 
    153 // Use SkBlurMask::BlurGroundTruth to blur a 'width' x 'height' solid
    154 // white rect. Return the right half of the middle row in 'result'.
    155 static void ground_truth_2d(int width, int height,
    156                             SkScalar sigma,
    157                             int* result, int resultCount) {
    158     SkMask src, dst;
    159 
    160     src.fBounds.set(0, 0, width, height);
    161     src.fFormat = SkMask::kA8_Format;
    162     src.fRowBytes = src.fBounds.width();
    163     src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
    164 
    165     memset(src.fImage, 0xff, src.computeTotalImageSize());
    166 
    167     if (!SkBlurMask::BlurGroundTruth(sigma, &dst, src, kNormal_SkBlurStyle)) {
    168         return;
    169     }
    170 
    171     int midX = dst.fBounds.centerX();
    172     int midY = dst.fBounds.centerY();
    173     uint8_t* bytes = dst.getAddr8(midX, midY);
    174     int i;
    175     for (i = 0; i < dst.fBounds.width()-(midX-dst.fBounds.fLeft); ++i) {
    176         if (i < resultCount) {
    177             result[i] = bytes[i];
    178         }
    179     }
    180     for ( ; i < resultCount; ++i) {
    181         result[i] = 0;
    182     }
    183 
    184     SkMask::FreeImage(src.fImage);
    185     SkMask::FreeImage(dst.fImage);
    186 }
    187 
    188 // Implement a step function that is 255 between min and max; 0 elsewhere.
    189 static int step(int x, SkScalar min, SkScalar max) {
    190     if (min < x && x < max) {
    191         return 255;
    192     }
    193     return 0;
    194 }
    195 
    196 // Implement a Gaussian function with 0 mean and std.dev. of 'sigma'.
    197 static float gaussian(int x, SkScalar sigma) {
    198     float k = SK_Scalar1/(sigma * sqrtf(2.0f*SK_ScalarPI));
    199     float exponent = -(x * x) / (2 * sigma * sigma);
    200     return k * expf(exponent);
    201 }
    202 
    203 // Perform a brute force convolution of a step function with a Gaussian.
    204 // Return the right half in 'result'
    205 static void brute_force_1d(SkScalar stepMin, SkScalar stepMax,
    206                            SkScalar gaussianSigma,
    207                            int* result, int resultCount) {
    208 
    209     int gaussianRange = SkScalarCeilToInt(10 * gaussianSigma);
    210 
    211     for (int i = 0; i < resultCount; ++i) {
    212         SkScalar sum = 0.0f;
    213         for (int j = -gaussianRange; j < gaussianRange; ++j) {
    214             sum += gaussian(j, gaussianSigma) * step(i-j, stepMin, stepMax);
    215         }
    216 
    217         result[i] = SkClampMax(SkClampPos(int(sum + 0.5f)), 255);
    218     }
    219 }
    220 
    221 static void blur_path(SkCanvas* canvas, const SkPath& path,
    222                       SkScalar gaussianSigma) {
    223 
    224     SkScalar midX = path.getBounds().centerX();
    225     SkScalar midY = path.getBounds().centerY();
    226 
    227     canvas->translate(-midX, -midY);
    228 
    229     SkPaint blurPaint;
    230     blurPaint.setColor(SK_ColorWHITE);
    231     blurPaint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, gaussianSigma,
    232                                                    SkBlurMaskFilter::kHighQuality_BlurFlag));
    233 
    234     canvas->drawColor(SK_ColorBLACK);
    235     canvas->drawPath(path, blurPaint);
    236 }
    237 
    238 // Readback the blurred draw results from the canvas
    239 static void readback(SkCanvas* canvas, int* result, int resultCount) {
    240     SkBitmap readback;
    241     readback.allocN32Pixels(resultCount, 30);
    242     canvas->readPixels(readback, 0, 0);
    243 
    244     SkPMColor* pixels = (SkPMColor*) readback.getAddr32(0, 15);
    245 
    246     for (int i = 0; i < resultCount; ++i) {
    247         result[i] = SkColorGetR(pixels[i]);
    248     }
    249 }
    250 
    251 // Draw a blurred version of the provided path.
    252 // Return the right half of the middle row in 'result'.
    253 static void cpu_blur_path(const SkPath& path, SkScalar gaussianSigma,
    254                           int* result, int resultCount) {
    255 
    256     SkBitmap bitmap;
    257     bitmap.allocN32Pixels(resultCount, 30);
    258     SkCanvas canvas(bitmap);
    259 
    260     blur_path(&canvas, path, gaussianSigma);
    261     readback(&canvas, result, resultCount);
    262 }
    263 
    264 #if SK_SUPPORT_GPU
    265 #if 0
    266 // temporary disable; see below for explanation
    267 static bool gpu_blur_path(GrContext* context, const SkPath& path,
    268                           SkScalar gaussianSigma,
    269                           int* result, int resultCount) {
    270     GrSurfaceDesc desc;
    271     desc.fConfig = kSkia8888_GrPixelConfig;
    272     desc.fFlags = kRenderTarget_GrSurfaceFlag;
    273     desc.fWidth = resultCount;
    274     desc.fHeight = 30;
    275     desc.fSampleCnt = 0;
    276 
    277     sk_sp<GrTexture> texture(grContext->createTexture(desc, false, nullptr, 0));
    278     sk_sp<SkGpuDevice> device(new SkGpuDevice(grContext, texture.get()));
    279     SkCanvas canvas(device.get());
    280 
    281     blur_path(&canvas, path, gaussianSigma);
    282     readback(&canvas, result, resultCount);
    283     return true;
    284 }
    285 #endif
    286 #endif
    287 
    288 #if WRITE_CSV
    289 static void write_as_csv(const char* label, SkScalar scale, int* data, int count) {
    290     SkDebugf("%s_%.2f,", label, scale);
    291     for (int i = 0; i < count-1; ++i) {
    292         SkDebugf("%d,", data[i]);
    293     }
    294     SkDebugf("%d\n", data[count-1]);
    295 }
    296 #endif
    297 
    298 static bool match(int* first, int* second, int count, int tol) {
    299     int delta;
    300     for (int i = 0; i < count; ++i) {
    301         delta = first[i] - second[i];
    302         if (delta > tol || delta < -tol) {
    303             return false;
    304         }
    305     }
    306 
    307     return true;
    308 }
    309 
    310 // Test out the normal blur style with a wide range of sigmas
    311 DEF_TEST(BlurSigmaRange, reporter) {
    312     static const int kSize = 100;
    313 
    314     // The geometry is offset a smidge to trigger:
    315     // https://code.google.com/p/chromium/issues/detail?id=282418
    316     SkPath rectPath;
    317     rectPath.addRect(0.3f, 0.3f, 100.3f, 100.3f);
    318 
    319     SkPoint polyPts[] = {
    320         { 0.3f, 0.3f },
    321         { 100.3f, 0.3f },
    322         { 100.3f, 100.3f },
    323         { 0.3f, 100.3f },
    324         { 2.3f, 50.3f }     // a little divet to throw off the rect special case
    325     };
    326     SkPath polyPath;
    327     polyPath.addPoly(polyPts, SK_ARRAY_COUNT(polyPts), true);
    328 
    329     int rectSpecialCaseResult[kSize];
    330     int generalCaseResult[kSize];
    331     int groundTruthResult[kSize];
    332     int bruteForce1DResult[kSize];
    333 
    334     SkScalar sigma = 10.0f;
    335 
    336     for (int i = 0; i < 4; ++i, sigma /= 10) {
    337 
    338         cpu_blur_path(rectPath, sigma, rectSpecialCaseResult, kSize);
    339         cpu_blur_path(polyPath, sigma, generalCaseResult, kSize);
    340 
    341         ground_truth_2d(100, 100, sigma, groundTruthResult, kSize);
    342         brute_force_1d(-50.0f, 50.0f, sigma, bruteForce1DResult, kSize);
    343 
    344         REPORTER_ASSERT(reporter, match(rectSpecialCaseResult, bruteForce1DResult, kSize, 5));
    345         REPORTER_ASSERT(reporter, match(generalCaseResult, bruteForce1DResult, kSize, 15));
    346 #if SK_SUPPORT_GPU
    347 #if 0
    348         int gpuResult[kSize];
    349         bool haveGPUResult = gpu_blur_path(context, rectPath, sigma, gpuResult, kSize);
    350         // Disabling this test for now -- I don't think it's a legit comparison.
    351         // Will continue to investigate this.
    352         if (haveGPUResult) {
    353             // 1 works everywhere but: Ubuntu13 & Nexus4
    354             REPORTER_ASSERT(reporter, match(gpuResult, bruteForce1DResult, kSize, 10));
    355         }
    356 #endif
    357 #endif
    358         REPORTER_ASSERT(reporter, match(groundTruthResult, bruteForce1DResult, kSize, 1));
    359 
    360 #if WRITE_CSV
    361         write_as_csv("RectSpecialCase", sigma, rectSpecialCaseResult, kSize);
    362         write_as_csv("GeneralCase", sigma, generalCaseResult, kSize);
    363 #if SK_SUPPORT_GPU
    364         write_as_csv("GPU", sigma, gpuResult, kSize);
    365 #endif
    366         write_as_csv("GroundTruth2D", sigma, groundTruthResult, kSize);
    367         write_as_csv("BruteForce1D", sigma, bruteForce1DResult, kSize);
    368 #endif
    369     }
    370 }
    371 
    372 ///////////////////////////////////////////////////////////////////////////////////////////
    373 
    374 static SkBlurQuality blurMaskFilterFlags_as_quality(uint32_t blurMaskFilterFlags) {
    375     return (blurMaskFilterFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
    376             kHigh_SkBlurQuality : kLow_SkBlurQuality;
    377 }
    378 
    379 static void test_blurDrawLooper(skiatest::Reporter* reporter, SkScalar sigma,
    380                                 SkBlurStyle style, uint32_t blurMaskFilterFlags) {
    381     if (kNormal_SkBlurStyle != style) {
    382         return; // blurdrawlooper only supports normal
    383     }
    384 
    385     const SkColor color = 0xFF335577;
    386     const SkScalar dx = 10;
    387     const SkScalar dy = -5;
    388     sk_sp<SkDrawLooper> lp(SkBlurDrawLooper::Make(color, sigma, dx, dy));
    389     const bool expectSuccess = sigma > 0;
    390 
    391     if (nullptr == lp) {
    392         REPORTER_ASSERT(reporter, sigma <= 0);
    393     } else {
    394         SkDrawLooper::BlurShadowRec rec;
    395         bool success = lp->asABlurShadow(&rec);
    396         REPORTER_ASSERT(reporter, success == expectSuccess);
    397         if (success) {
    398             REPORTER_ASSERT(reporter, rec.fSigma == sigma);
    399             REPORTER_ASSERT(reporter, rec.fOffset.x() == dx);
    400             REPORTER_ASSERT(reporter, rec.fOffset.y() == dy);
    401             REPORTER_ASSERT(reporter, rec.fColor == color);
    402             REPORTER_ASSERT(reporter, rec.fStyle == style);
    403             REPORTER_ASSERT(reporter, rec.fQuality == kLow_SkBlurQuality);
    404         }
    405     }
    406 }
    407 
    408 static void test_looper(skiatest::Reporter* reporter, sk_sp<SkDrawLooper> lp, SkScalar sigma,
    409                         SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) {
    410     SkDrawLooper::BlurShadowRec rec;
    411     bool success = lp->asABlurShadow(&rec);
    412     REPORTER_ASSERT(reporter, success == expectSuccess);
    413     if (success != expectSuccess) {
    414         lp->asABlurShadow(&rec);
    415     }
    416     if (success) {
    417         REPORTER_ASSERT(reporter, rec.fSigma == sigma);
    418         REPORTER_ASSERT(reporter, rec.fStyle == style);
    419         REPORTER_ASSERT(reporter, rec.fQuality == quality);
    420     }
    421 }
    422 
    423 static void make_noop_layer(SkLayerDrawLooper::Builder* builder) {
    424     SkLayerDrawLooper::LayerInfo info;
    425 
    426     info.fPaintBits = 0;
    427     info.fColorMode = SkBlendMode::kDst;
    428     builder->addLayer(info);
    429 }
    430 
    431 static void make_blur_layer(SkLayerDrawLooper::Builder* builder, sk_sp<SkMaskFilter> mf) {
    432     SkLayerDrawLooper::LayerInfo info;
    433 
    434     info.fPaintBits = SkLayerDrawLooper::kMaskFilter_Bit;
    435     info.fColorMode = SkBlendMode::kSrc;
    436     SkPaint* paint = builder->addLayer(info);
    437     paint->setMaskFilter(std::move(mf));
    438 }
    439 
    440 static void test_layerDrawLooper(skiatest::Reporter* reporter, sk_sp<SkMaskFilter> mf,
    441                                  SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
    442                                  bool expectSuccess) {
    443 
    444     SkLayerDrawLooper::LayerInfo info;
    445     SkLayerDrawLooper::Builder builder;
    446 
    447     // 1 layer is too few
    448     make_noop_layer(&builder);
    449     test_looper(reporter, builder.detach(), sigma, style, quality, false);
    450 
    451     // 2 layers is good, but need blur
    452     make_noop_layer(&builder);
    453     make_noop_layer(&builder);
    454     test_looper(reporter, builder.detach(), sigma, style, quality, false);
    455 
    456     // 2 layers is just right
    457     make_noop_layer(&builder);
    458     make_blur_layer(&builder, mf);
    459     test_looper(reporter, builder.detach(), sigma, style, quality, expectSuccess);
    460 
    461     // 3 layers is too many
    462     make_noop_layer(&builder);
    463     make_blur_layer(&builder, mf);
    464     make_noop_layer(&builder);
    465     test_looper(reporter, builder.detach(), sigma, style, quality, false);
    466 }
    467 
    468 DEF_TEST(BlurAsABlur, reporter) {
    469     const SkBlurStyle styles[] = {
    470         kNormal_SkBlurStyle, kSolid_SkBlurStyle, kOuter_SkBlurStyle, kInner_SkBlurStyle
    471     };
    472     const SkScalar sigmas[] = {
    473         // values <= 0 should not success for a blur
    474         -1, 0, 0.5f, 2
    475     };
    476 
    477     // Test asABlur for SkBlurMaskFilter
    478     //
    479     for (size_t i = 0; i < SK_ARRAY_COUNT(styles); ++i) {
    480         const SkBlurStyle style = styles[i];
    481         for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) {
    482             const SkScalar sigma = sigmas[j];
    483             for (int flags = 0; flags <= SkBlurMaskFilter::kAll_BlurFlag; ++flags) {
    484                 const SkBlurQuality quality = blurMaskFilterFlags_as_quality(flags);
    485 
    486                 sk_sp<SkMaskFilter> mf(SkBlurMaskFilter::Make(style, sigma, flags));
    487                 if (nullptr == mf.get()) {
    488                     REPORTER_ASSERT(reporter, sigma <= 0);
    489                 } else {
    490                     REPORTER_ASSERT(reporter, sigma > 0);
    491                     SkMaskFilter::BlurRec rec;
    492                     bool success = mf->asABlur(&rec);
    493                     if (flags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
    494                         REPORTER_ASSERT(reporter, !success);
    495                     } else {
    496                         REPORTER_ASSERT(reporter, success);
    497                         REPORTER_ASSERT(reporter, rec.fSigma == sigma);
    498                         REPORTER_ASSERT(reporter, rec.fStyle == style);
    499                         REPORTER_ASSERT(reporter, rec.fQuality == quality);
    500                     }
    501                     test_layerDrawLooper(reporter, std::move(mf), sigma, style, quality, success);
    502                 }
    503                 test_blurDrawLooper(reporter, sigma, style, flags);
    504             }
    505         }
    506     }
    507 
    508     // Test asABlur for SkEmbossMaskFilter -- should never succeed
    509     //
    510     {
    511         SkEmbossMaskFilter::Light light = {
    512             { 1, 1, 1 }, 0, 127, 127
    513         };
    514         for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) {
    515             const SkScalar sigma = sigmas[j];
    516             auto mf(SkEmbossMaskFilter::Make(sigma, light));
    517             if (mf) {
    518                 SkMaskFilter::BlurRec rec;
    519                 bool success = mf->asABlur(&rec);
    520                 REPORTER_ASSERT(reporter, !success);
    521             }
    522         }
    523     }
    524 }
    525 
    526 #if SK_SUPPORT_GPU
    527 
    528 // This exercises the problem discovered in crbug.com/570232. The return value from
    529 // SkBlurMask::BoxBlur wasn't being checked in SkBlurMaskFilter.cpp::GrRRectBlurEffect::Create
    530 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SmallBoxBlurBug, reporter, ctxInfo) {
    531 
    532     SkImageInfo info = SkImageInfo::MakeN32Premul(128, 128);
    533     auto surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, info));
    534     SkCanvas* canvas = surface->getCanvas();
    535 
    536     SkRect r = SkRect::MakeXYWH(10, 10, 100, 100);
    537     SkRRect rr = SkRRect::MakeRectXY(r, 10, 10);
    538 
    539     SkPaint p;
    540     p.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 0.01f));
    541 
    542     canvas->drawRRect(rr, p);
    543 }
    544 
    545 #endif
    546 
    547 
    548 DEF_TEST(BlurredRRectNinePatchComputation, reporter) {
    549     const SkRect r = SkRect::MakeXYWH(10, 10, 100, 100);
    550     static const SkScalar kBlurRad = 3.0f;
    551 
    552     bool ninePatchable;
    553     SkRRect rrectToDraw;
    554     SkISize size;
    555     SkScalar rectXs[SkBlurMaskFilter::kMaxDivisions], rectYs[SkBlurMaskFilter::kMaxDivisions];
    556     SkScalar texXs[SkBlurMaskFilter::kMaxDivisions], texYs[SkBlurMaskFilter::kMaxDivisions];
    557     int numX, numY;
    558     uint32_t skipMask;
    559 
    560     // not nine-patchable
    561     {
    562         SkVector radii[4] = { { 100, 100 }, { 0, 0 }, { 100, 100 }, { 0, 0 } };
    563 
    564         SkRRect rr;
    565         rr.setRectRadii(r, radii);
    566 
    567         ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(),
    568                                                                     kBlurRad, kBlurRad,
    569                                                                     &rrectToDraw, &size,
    570                                                                     rectXs, rectYs, texXs, texYs,
    571                                                                     &numX, &numY, &skipMask);
    572         REPORTER_ASSERT(reporter, !ninePatchable);
    573     }
    574 
    575     // simple circular
    576     {
    577         static const SkScalar kCornerRad = 10.0f;
    578         SkRRect rr;
    579         rr.setRectXY(r, kCornerRad, kCornerRad);
    580 
    581         ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(),
    582                                                                     kBlurRad, kBlurRad,
    583                                                                     &rrectToDraw, &size,
    584                                                                     rectXs, rectYs, texXs, texYs,
    585                                                                     &numX, &numY, &skipMask);
    586 
    587         static const SkScalar kAns = 12.0f * kBlurRad + 2.0f * kCornerRad + 1.0f;
    588         REPORTER_ASSERT(reporter, ninePatchable);
    589         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kAns));
    590         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kAns));
    591         REPORTER_ASSERT(reporter, 4 == numX && 4 == numY);
    592         REPORTER_ASSERT(reporter, !skipMask);
    593     }
    594 
    595     // simple elliptical
    596     {
    597         static const SkScalar kXCornerRad = 2.0f;
    598         static const SkScalar kYCornerRad = 10.0f;
    599         SkRRect rr;
    600         rr.setRectXY(r, kXCornerRad, kYCornerRad);
    601 
    602         ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(),
    603                                                                     kBlurRad, kBlurRad,
    604                                                                     &rrectToDraw, &size,
    605                                                                     rectXs, rectYs, texXs, texYs,
    606                                                                     &numX, &numY, &skipMask);
    607 
    608         static const SkScalar kXAns = 12.0f * kBlurRad + 2.0f * kXCornerRad + 1.0f;
    609         static const SkScalar kYAns = 12.0f * kBlurRad + 2.0f * kYCornerRad + 1.0f;
    610 
    611         REPORTER_ASSERT(reporter, ninePatchable);
    612         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kXAns));
    613         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kYAns));
    614         REPORTER_ASSERT(reporter, 4 == numX && 4 == numY);
    615         REPORTER_ASSERT(reporter, !skipMask);
    616     }
    617 
    618     // test-out occlusion
    619     {
    620         static const SkScalar kCornerRad = 10.0f;
    621         SkRRect rr;
    622         rr.setRectXY(r, kCornerRad, kCornerRad);
    623 
    624         // The rectXs & rectYs should be { 1, 29, 91, 119 }. Add two more points around each.
    625         SkScalar testLocs[] = {
    626              -18.0f, -9.0f,
    627                1.0f,
    628                9.0f, 18.0f,
    629               29.0f,
    630               39.0f, 49.0f,
    631               91.0f,
    632              109.0f, 118.0f,
    633              119.0f,
    634              139.0f, 149.0f
    635         };
    636 
    637         for (int minY = 0; minY < (int)SK_ARRAY_COUNT(testLocs); ++minY) {
    638             for (int maxY = minY+1; maxY < (int)SK_ARRAY_COUNT(testLocs); ++maxY) {
    639                 for (int minX = 0; minX < (int)SK_ARRAY_COUNT(testLocs); ++minX) {
    640                     for (int maxX = minX+1; maxX < (int)SK_ARRAY_COUNT(testLocs); ++maxX) {
    641                         SkRect occluder = SkRect::MakeLTRB(testLocs[minX], testLocs[minY],
    642                                                            testLocs[maxX], testLocs[maxY]);
    643                         if (occluder.isEmpty()) {
    644                             continue;
    645                         }
    646 
    647                         ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(
    648                                                                     rr, rr, occluder,
    649                                                                     kBlurRad, kBlurRad,
    650                                                                     &rrectToDraw, &size,
    651                                                                     rectXs, rectYs, texXs, texYs,
    652                                                                     &numX, &numY, &skipMask);
    653 
    654                         static const SkScalar kAns = 12.0f * kBlurRad + 2.0f * kCornerRad + 1.0f;
    655                         REPORTER_ASSERT(reporter, ninePatchable);
    656                         REPORTER_ASSERT(reporter,
    657                                             SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kAns));
    658                         REPORTER_ASSERT(reporter,
    659                                             SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kAns));
    660 
    661                         int checkBit = 0x1;
    662                         for (int y = 0; y < numY-1; ++y) {
    663                             for (int x = 0; x < numX-1; ++x) {
    664                                 SkRect cell = SkRect::MakeLTRB(rectXs[x], rectYs[y],
    665                                                                rectXs[x+1], rectYs[y+1]);
    666                                 REPORTER_ASSERT(reporter,
    667                                                     SkToBool(skipMask & checkBit) ==
    668                                                     (cell.isEmpty() || occluder.contains(cell)));
    669 
    670                                 REPORTER_ASSERT(reporter, texXs[x] >= 0 &&
    671                                                           texXs[x] <= size.fWidth);
    672                                 REPORTER_ASSERT(reporter, texYs[y] >= 0 &&
    673                                                           texXs[y] <= size.fHeight);
    674 
    675                                 checkBit <<= 1;
    676                             }
    677                         }
    678                     }
    679                 }
    680             }
    681         }
    682 
    683 
    684     }
    685 
    686 }
    687 
    688 ///////////////////////////////////////////////////////////////////////////////////////////
    689