Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2013 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 "SkBitmap.h"
      9 #include "SkBlurImageFilter.h"
     10 #include "SkCanvas.h"
     11 #include "SkColorFilterImageFilter.h"
     12 #include "SkColorMatrixFilter.h"
     13 #include "SkComposeImageFilter.h"
     14 #include "SkDisplacementMapEffect.h"
     15 #include "SkDropShadowImageFilter.h"
     16 #include "SkFlattenableSerialization.h"
     17 #include "SkGradientShader.h"
     18 #include "SkImage.h"
     19 #include "SkImageSource.h"
     20 #include "SkLightingImageFilter.h"
     21 #include "SkMatrixConvolutionImageFilter.h"
     22 #include "SkMergeImageFilter.h"
     23 #include "SkMorphologyImageFilter.h"
     24 #include "SkOffsetImageFilter.h"
     25 #include "SkPaintImageFilter.h"
     26 #include "SkPerlinNoiseShader.h"
     27 #include "SkPicture.h"
     28 #include "SkPictureImageFilter.h"
     29 #include "SkPictureRecorder.h"
     30 #include "SkPoint3.h"
     31 #include "SkReadBuffer.h"
     32 #include "SkRect.h"
     33 #include "SkSpecialImage.h"
     34 #include "SkSpecialSurface.h"
     35 #include "SkSurface.h"
     36 #include "SkTableColorFilter.h"
     37 #include "SkTileImageFilter.h"
     38 #include "SkXfermodeImageFilter.h"
     39 #include "Test.h"
     40 
     41 #if SK_SUPPORT_GPU
     42 #include "GrContext.h"
     43 #endif
     44 
     45 static const int kBitmapSize = 4;
     46 
     47 namespace {
     48 
     49 class MatrixTestImageFilter : public SkImageFilter {
     50 public:
     51     static sk_sp<SkImageFilter> Make(skiatest::Reporter* reporter,
     52                                      const SkMatrix& expectedMatrix) {
     53         return sk_sp<SkImageFilter>(new MatrixTestImageFilter(reporter, expectedMatrix));
     54     }
     55 
     56     SK_TO_STRING_OVERRIDE()
     57     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(MatrixTestImageFilter)
     58 
     59 protected:
     60     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context& ctx,
     61                                         SkIPoint* offset) const override {
     62         REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
     63         offset->fX = offset->fY = 0;
     64         return sk_ref_sp<SkSpecialImage>(source);
     65     }
     66 
     67     void flatten(SkWriteBuffer& buffer) const override {
     68         SkDEBUGFAIL("Should never get here");
     69     }
     70 
     71 private:
     72     MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix)
     73         : INHERITED(nullptr, 0, nullptr)
     74         , fReporter(reporter)
     75         , fExpectedMatrix(expectedMatrix) {
     76     }
     77 
     78     skiatest::Reporter* fReporter;
     79     SkMatrix fExpectedMatrix;
     80 
     81     typedef SkImageFilter INHERITED;
     82 };
     83 
     84 class FailImageFilter : public SkImageFilter {
     85 public:
     86     FailImageFilter() : SkImageFilter(nullptr, 0, nullptr) { }
     87 
     88     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source,
     89                                         const Context& ctx,
     90                                         SkIPoint* offset) const override {
     91         return nullptr;
     92     }
     93 
     94     SK_TO_STRING_OVERRIDE()
     95     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(FailImageFilter)
     96 
     97 private:
     98     typedef SkImageFilter INHERITED;
     99 };
    100 
    101 sk_sp<SkFlattenable> FailImageFilter::CreateProc(SkReadBuffer& buffer) {
    102     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
    103     return sk_sp<SkFlattenable>(new FailImageFilter());
    104 }
    105 
    106 #ifndef SK_IGNORE_TO_STRING
    107 void FailImageFilter::toString(SkString* str) const {
    108     str->appendf("FailImageFilter: (");
    109     str->append(")");
    110 }
    111 #endif
    112 
    113 void draw_gradient_circle(SkCanvas* canvas, int width, int height) {
    114     SkScalar x = SkIntToScalar(width / 2);
    115     SkScalar y = SkIntToScalar(height / 2);
    116     SkScalar radius = SkMinScalar(x, y) * 0.8f;
    117     canvas->clear(0x00000000);
    118     SkColor colors[2];
    119     colors[0] = SK_ColorWHITE;
    120     colors[1] = SK_ColorBLACK;
    121     sk_sp<SkShader> shader(
    122         SkGradientShader::MakeRadial(SkPoint::Make(x, y), radius, colors, nullptr, 2,
    123                                        SkShader::kClamp_TileMode)
    124     );
    125     SkPaint paint;
    126     paint.setShader(shader);
    127     canvas->drawCircle(x, y, radius, paint);
    128 }
    129 
    130 SkBitmap make_gradient_circle(int width, int height) {
    131     SkBitmap bitmap;
    132     bitmap.allocN32Pixels(width, height);
    133     SkCanvas canvas(bitmap);
    134     draw_gradient_circle(&canvas, width, height);
    135     return bitmap;
    136 }
    137 
    138 class FilterList {
    139 public:
    140     FilterList(sk_sp<SkImageFilter> input, const SkImageFilter::CropRect* cropRect = nullptr) {
    141         SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
    142         const SkScalar five = SkIntToScalar(5);
    143 
    144         {
    145             sk_sp<SkColorFilter> cf(SkColorFilter::MakeModeFilter(SK_ColorRED,
    146                                                                   SkBlendMode::kSrcIn));
    147 
    148             this->addFilter("color filter",
    149                 SkColorFilterImageFilter::Make(std::move(cf), input, cropRect));
    150         }
    151 
    152         {
    153             sk_sp<SkImage> gradientImage(SkImage::MakeFromBitmap(make_gradient_circle(64, 64)));
    154             sk_sp<SkImageFilter> gradientSource(SkImageSource::Make(std::move(gradientImage)));
    155 
    156             this->addFilter("displacement map",
    157                 SkDisplacementMapEffect::Make(SkDisplacementMapEffect::kR_ChannelSelectorType,
    158                                               SkDisplacementMapEffect::kB_ChannelSelectorType,
    159                                               20.0f,
    160                                               std::move(gradientSource), input, cropRect));
    161         }
    162 
    163         this->addFilter("blur", SkBlurImageFilter::Make(SK_Scalar1,
    164                                                         SK_Scalar1,
    165                                                         input,
    166                                                         cropRect));
    167         this->addFilter("drop shadow", SkDropShadowImageFilter::Make(
    168                   SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN,
    169                   SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
    170                   input, cropRect));
    171         this->addFilter("diffuse lighting",
    172                   SkLightingImageFilter::MakePointLitDiffuse(location, SK_ColorGREEN, 0, 0,
    173                                                              input, cropRect));
    174         this->addFilter("specular lighting",
    175                   SkLightingImageFilter::MakePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0,
    176                                                               input, cropRect));
    177         {
    178             SkScalar kernel[9] = {
    179                 SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1),
    180                 SkIntToScalar(1), SkIntToScalar(-7), SkIntToScalar(1),
    181                 SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1),
    182             };
    183             const SkISize kernelSize = SkISize::Make(3, 3);
    184             const SkScalar gain = SK_Scalar1, bias = 0;
    185 
    186             this->addFilter("matrix convolution",
    187                   SkMatrixConvolutionImageFilter::Make(
    188                       kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
    189                       SkMatrixConvolutionImageFilter::kRepeat_TileMode, false,
    190                       input, cropRect));
    191         }
    192 
    193         this->addFilter("merge", SkMergeImageFilter::Make(input, input,
    194                                                           SkBlendMode::kSrcOver,
    195                                                           cropRect));
    196 
    197         {
    198             SkPaint greenColorShaderPaint;
    199             greenColorShaderPaint.setShader(SkShader::MakeColorShader(SK_ColorGREEN));
    200 
    201             SkImageFilter::CropRect leftSideCropRect(SkRect::MakeXYWH(0, 0, 32, 64));
    202             sk_sp<SkImageFilter> paintFilterLeft(SkPaintImageFilter::Make(greenColorShaderPaint,
    203                                                                           &leftSideCropRect));
    204             SkImageFilter::CropRect rightSideCropRect(SkRect::MakeXYWH(32, 0, 32, 64));
    205             sk_sp<SkImageFilter> paintFilterRight(SkPaintImageFilter::Make(greenColorShaderPaint,
    206                                                                            &rightSideCropRect));
    207 
    208 
    209             this->addFilter("merge with disjoint inputs", SkMergeImageFilter::Make(
    210                   std::move(paintFilterLeft), std::move(paintFilterRight),
    211                   SkBlendMode::kSrcOver, cropRect));
    212         }
    213 
    214         this->addFilter("offset",
    215                         SkOffsetImageFilter::Make(SK_Scalar1, SK_Scalar1, input,
    216                                                   cropRect));
    217         this->addFilter("dilate", SkDilateImageFilter::Make(3, 2, input, cropRect));
    218         this->addFilter("erode", SkErodeImageFilter::Make(2, 3, input, cropRect));
    219         this->addFilter("tile", SkTileImageFilter::Make(
    220                                     SkRect::MakeXYWH(0, 0, 50, 50),
    221                                     cropRect ? cropRect->rect() : SkRect::MakeXYWH(0, 0, 100, 100),
    222                                     input));
    223 
    224         if (!cropRect) {
    225             SkMatrix matrix;
    226 
    227             matrix.setTranslate(SK_Scalar1, SK_Scalar1);
    228             matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
    229 
    230             this->addFilter("matrix",
    231                 SkImageFilter::MakeMatrixFilter(matrix, kLow_SkFilterQuality, input));
    232         }
    233         {
    234             sk_sp<SkImageFilter> blur(SkBlurImageFilter::Make(five, five, input));
    235 
    236             this->addFilter("blur and offset", SkOffsetImageFilter::Make(five, five,
    237                                                                          std::move(blur),
    238                                                                          cropRect));
    239         }
    240         {
    241             SkRTreeFactory factory;
    242             SkPictureRecorder recorder;
    243             SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0);
    244 
    245             SkPaint greenPaint;
    246             greenPaint.setColor(SK_ColorGREEN);
    247             recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
    248             sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
    249             sk_sp<SkImageFilter> pictureFilter(SkPictureImageFilter::Make(std::move(picture)));
    250 
    251             this->addFilter("picture and blur", SkBlurImageFilter::Make(five, five,
    252                                                                         std::move(pictureFilter),
    253                                                                         cropRect));
    254         }
    255         {
    256             SkPaint paint;
    257             paint.setShader(SkPerlinNoiseShader::MakeTurbulence(SK_Scalar1, SK_Scalar1, 1, 0));
    258             sk_sp<SkImageFilter> paintFilter(SkPaintImageFilter::Make(paint));
    259 
    260             this->addFilter("paint and blur", SkBlurImageFilter::Make(five, five,
    261                                                                       std::move(paintFilter),
    262                                                                       cropRect));
    263         }
    264         this->addFilter("xfermode", SkXfermodeImageFilter::Make(SkBlendMode::kSrc, input, input,
    265                                                                 cropRect));
    266     }
    267     int count() const { return fFilters.count(); }
    268     SkImageFilter* getFilter(int index) const { return fFilters[index].fFilter.get(); }
    269     const char* getName(int index) const { return fFilters[index].fName; }
    270 private:
    271     struct Filter {
    272         Filter() : fName(nullptr) {}
    273         Filter(const char* name, sk_sp<SkImageFilter> filter)
    274             : fName(name)
    275             , fFilter(std::move(filter)) {
    276         }
    277         const char*                 fName;
    278         sk_sp<SkImageFilter>        fFilter;
    279     };
    280     void addFilter(const char* name, sk_sp<SkImageFilter> filter) {
    281         fFilters.push_back(Filter(name, std::move(filter)));
    282     }
    283 
    284     SkTArray<Filter> fFilters;
    285 };
    286 
    287 }
    288 
    289 sk_sp<SkFlattenable> MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) {
    290     SkDEBUGFAIL("Should never get here");
    291     return nullptr;
    292 }
    293 
    294 #ifndef SK_IGNORE_TO_STRING
    295 void MatrixTestImageFilter::toString(SkString* str) const {
    296     str->appendf("MatrixTestImageFilter: (");
    297     str->append(")");
    298 }
    299 #endif
    300 
    301 static sk_sp<SkImage> make_small_image() {
    302     auto surface(SkSurface::MakeRasterN32Premul(kBitmapSize, kBitmapSize));
    303     SkCanvas* canvas = surface->getCanvas();
    304     canvas->clear(0x00000000);
    305     SkPaint darkPaint;
    306     darkPaint.setColor(0xFF804020);
    307     SkPaint lightPaint;
    308     lightPaint.setColor(0xFF244484);
    309     const int i = kBitmapSize / 4;
    310     for (int y = 0; y < kBitmapSize; y += i) {
    311         for (int x = 0; x < kBitmapSize; x += i) {
    312             canvas->save();
    313             canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
    314             canvas->drawRect(SkRect::MakeXYWH(0, 0,
    315                                              SkIntToScalar(i),
    316                                              SkIntToScalar(i)), darkPaint);
    317             canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
    318                                              0,
    319                                              SkIntToScalar(i),
    320                                              SkIntToScalar(i)), lightPaint);
    321             canvas->drawRect(SkRect::MakeXYWH(0,
    322                                              SkIntToScalar(i),
    323                                              SkIntToScalar(i),
    324                                              SkIntToScalar(i)), lightPaint);
    325             canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
    326                                              SkIntToScalar(i),
    327                                              SkIntToScalar(i),
    328                                              SkIntToScalar(i)), darkPaint);
    329             canvas->restore();
    330         }
    331     }
    332 
    333     return surface->makeImageSnapshot();
    334 }
    335 
    336 static sk_sp<SkImageFilter> make_scale(float amount, sk_sp<SkImageFilter> input) {
    337     SkScalar s = amount;
    338     SkScalar matrix[20] = { s, 0, 0, 0, 0,
    339                             0, s, 0, 0, 0,
    340                             0, 0, s, 0, 0,
    341                             0, 0, 0, s, 0 };
    342     sk_sp<SkColorFilter> filter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix));
    343     return SkColorFilterImageFilter::Make(std::move(filter), std::move(input));
    344 }
    345 
    346 static sk_sp<SkImageFilter> make_grayscale(sk_sp<SkImageFilter> input,
    347                                            const SkImageFilter::CropRect* cropRect) {
    348     SkScalar matrix[20];
    349     memset(matrix, 0, 20 * sizeof(SkScalar));
    350     matrix[0] = matrix[5] = matrix[10] = 0.2126f;
    351     matrix[1] = matrix[6] = matrix[11] = 0.7152f;
    352     matrix[2] = matrix[7] = matrix[12] = 0.0722f;
    353     matrix[18] = 1.0f;
    354     sk_sp<SkColorFilter> filter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix));
    355     return SkColorFilterImageFilter::Make(std::move(filter), std::move(input), cropRect);
    356 }
    357 
    358 static sk_sp<SkImageFilter> make_blue(sk_sp<SkImageFilter> input,
    359                                       const SkImageFilter::CropRect* cropRect) {
    360     sk_sp<SkColorFilter> filter(SkColorFilter::MakeModeFilter(SK_ColorBLUE,
    361                                                               SkBlendMode::kSrcIn));
    362     return SkColorFilterImageFilter::Make(std::move(filter), std::move(input), cropRect);
    363 }
    364 
    365 static sk_sp<SkSpecialSurface> create_empty_special_surface(GrContext* context, int widthHeight) {
    366 #if SK_SUPPORT_GPU
    367     if (context) {
    368         return SkSpecialSurface::MakeRenderTarget(context,
    369                                                   widthHeight, widthHeight,
    370                                                   kRGBA_8888_GrPixelConfig, nullptr);
    371     } else
    372 #endif
    373     {
    374         const SkImageInfo info = SkImageInfo::MakeN32(widthHeight, widthHeight,
    375                                                       kOpaque_SkAlphaType);
    376         return SkSpecialSurface::MakeRaster(info);
    377     }
    378 }
    379 
    380 static sk_sp<SkSurface> create_surface(GrContext* context, int width, int height) {
    381     const SkImageInfo info = SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType);
    382 #if SK_SUPPORT_GPU
    383     if (context) {
    384         return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
    385     } else
    386 #endif
    387     {
    388         return SkSurface::MakeRaster(info);
    389     }
    390 }
    391 
    392 static sk_sp<SkSpecialImage> create_empty_special_image(GrContext* context, int widthHeight) {
    393     sk_sp<SkSpecialSurface> surf(create_empty_special_surface(context, widthHeight));
    394 
    395     SkASSERT(surf);
    396 
    397     SkCanvas* canvas = surf->getCanvas();
    398     SkASSERT(canvas);
    399 
    400     canvas->clear(0x0);
    401 
    402     return surf->makeImageSnapshot();
    403 }
    404 
    405 
    406 DEF_TEST(ImageFilter, reporter) {
    407     {
    408         // Check that two non-clipping color-matrice-filters concatenate into a single filter.
    409         sk_sp<SkImageFilter> halfBrightness(make_scale(0.5f, nullptr));
    410         sk_sp<SkImageFilter> quarterBrightness(make_scale(0.5f, std::move(halfBrightness)));
    411         REPORTER_ASSERT(reporter, nullptr == quarterBrightness->getInput(0));
    412         SkColorFilter* cf;
    413         REPORTER_ASSERT(reporter, quarterBrightness->asColorFilter(&cf));
    414         REPORTER_ASSERT(reporter, cf->asColorMatrix(nullptr));
    415         cf->unref();
    416     }
    417 
    418     {
    419         // Check that a clipping color-matrice-filter followed by a color-matrice-filters
    420         // concatenates into a single filter, but not a matrixfilter (due to clamping).
    421         sk_sp<SkImageFilter> doubleBrightness(make_scale(2.0f, nullptr));
    422         sk_sp<SkImageFilter> halfBrightness(make_scale(0.5f, std::move(doubleBrightness)));
    423         REPORTER_ASSERT(reporter, nullptr == halfBrightness->getInput(0));
    424         SkColorFilter* cf;
    425         REPORTER_ASSERT(reporter, halfBrightness->asColorFilter(&cf));
    426         REPORTER_ASSERT(reporter, !cf->asColorMatrix(nullptr));
    427         cf->unref();
    428     }
    429 
    430     {
    431         // Check that a color filter image filter without a crop rect can be
    432         // expressed as a color filter.
    433         sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
    434         REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
    435     }
    436 
    437     {
    438         // Check that a colorfilterimage filter without a crop rect but with an input
    439         // that is another colorfilterimage can be expressed as a colorfilter (composed).
    440         sk_sp<SkImageFilter> mode(make_blue(nullptr, nullptr));
    441         sk_sp<SkImageFilter> gray(make_grayscale(std::move(mode), nullptr));
    442         REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
    443     }
    444 
    445     {
    446         // Test that if we exceed the limit of what ComposeColorFilter can combine, we still
    447         // can build the DAG and won't assert if we call asColorFilter.
    448         sk_sp<SkImageFilter> filter(make_blue(nullptr, nullptr));
    449         const int kWayTooManyForComposeColorFilter = 100;
    450         for (int i = 0; i < kWayTooManyForComposeColorFilter; ++i) {
    451             filter = make_blue(filter, nullptr);
    452             // the first few of these will succeed, but after we hit the internal limit,
    453             // it will then return false.
    454             (void)filter->asColorFilter(nullptr);
    455         }
    456     }
    457 
    458     {
    459         // Check that a color filter image filter with a crop rect cannot
    460         // be expressed as a color filter.
    461         SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 100));
    462         sk_sp<SkImageFilter> grayWithCrop(make_grayscale(nullptr, &cropRect));
    463         REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(nullptr));
    464     }
    465 
    466     {
    467         // Check that two non-commutative matrices are concatenated in
    468         // the correct order.
    469         SkScalar blueToRedMatrix[20] = { 0 };
    470         blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
    471         SkScalar redToGreenMatrix[20] = { 0 };
    472         redToGreenMatrix[5] = redToGreenMatrix[18] = SK_Scalar1;
    473         sk_sp<SkColorFilter> blueToRed(SkColorFilter::MakeMatrixFilterRowMajor255(blueToRedMatrix));
    474         sk_sp<SkImageFilter> filter1(SkColorFilterImageFilter::Make(std::move(blueToRed),
    475                                                                     nullptr));
    476         sk_sp<SkColorFilter> redToGreen(SkColorFilter::MakeMatrixFilterRowMajor255(redToGreenMatrix));
    477         sk_sp<SkImageFilter> filter2(SkColorFilterImageFilter::Make(std::move(redToGreen),
    478                                                                     std::move(filter1)));
    479 
    480         SkBitmap result;
    481         result.allocN32Pixels(kBitmapSize, kBitmapSize);
    482 
    483         SkPaint paint;
    484         paint.setColor(SK_ColorBLUE);
    485         paint.setImageFilter(std::move(filter2));
    486         SkCanvas canvas(result);
    487         canvas.clear(0x0);
    488         SkRect rect = SkRect::Make(SkIRect::MakeWH(kBitmapSize, kBitmapSize));
    489         canvas.drawRect(rect, paint);
    490         uint32_t pixel = *result.getAddr32(0, 0);
    491         // The result here should be green, since we have effectively shifted blue to green.
    492         REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
    493     }
    494 
    495     {
    496         // Tests pass by not asserting
    497         sk_sp<SkImage> image(make_small_image());
    498         SkBitmap result;
    499         result.allocN32Pixels(kBitmapSize, kBitmapSize);
    500 
    501         {
    502             // This tests for :
    503             // 1 ) location at (0,0,1)
    504             SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
    505             // 2 ) location and target at same value
    506             SkPoint3 target = SkPoint3::Make(location.fX, location.fY, location.fZ);
    507             // 3 ) large negative specular exponent value
    508             SkScalar specularExponent = -1000;
    509 
    510             sk_sp<SkImageFilter> bmSrc(SkImageSource::Make(std::move(image)));
    511             SkPaint paint;
    512             paint.setImageFilter(SkLightingImageFilter::MakeSpotLitSpecular(
    513                     location, target, specularExponent, 180,
    514                     0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1,
    515                     std::move(bmSrc)));
    516             SkCanvas canvas(result);
    517             SkRect r = SkRect::MakeWH(SkIntToScalar(kBitmapSize),
    518                                       SkIntToScalar(kBitmapSize));
    519             canvas.drawRect(r, paint);
    520         }
    521     }
    522 }
    523 
    524 static void test_crop_rects(skiatest::Reporter* reporter,
    525                             GrContext* context) {
    526     // Check that all filters offset to their absolute crop rect,
    527     // unaffected by the input crop rect.
    528     // Tests pass by not asserting.
    529     sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100));
    530     SkASSERT(srcImg);
    531 
    532     SkImageFilter::CropRect inputCropRect(SkRect::MakeXYWH(8, 13, 80, 80));
    533     SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 30, 60, 60));
    534     sk_sp<SkImageFilter> input(make_grayscale(nullptr, &inputCropRect));
    535 
    536     FilterList filters(input, &cropRect);
    537 
    538     for (int i = 0; i < filters.count(); ++i) {
    539         SkImageFilter* filter = filters.getFilter(i);
    540         SkIPoint offset;
    541         SkImageFilter::OutputProperties noColorSpace(nullptr);
    542         SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
    543         sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
    544         REPORTER_ASSERT_MESSAGE(reporter, resultImg, filters.getName(i));
    545         REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, filters.getName(i));
    546     }
    547 }
    548 
    549 static void test_negative_blur_sigma(skiatest::Reporter* reporter,
    550                                      GrContext* context) {
    551     // Check that SkBlurImageFilter will accept a negative sigma, either in
    552     // the given arguments or after CTM application.
    553     const int width = 32, height = 32;
    554     const SkScalar five = SkIntToScalar(5);
    555 
    556     sk_sp<SkImageFilter> positiveFilter(SkBlurImageFilter::Make(five, five, nullptr));
    557     sk_sp<SkImageFilter> negativeFilter(SkBlurImageFilter::Make(-five, five, nullptr));
    558 
    559     SkBitmap gradient = make_gradient_circle(width, height);
    560     sk_sp<SkSpecialImage> imgSrc(SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(width, height),
    561                                                                 gradient));
    562 
    563     SkIPoint offset;
    564     SkImageFilter::OutputProperties noColorSpace(nullptr);
    565     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr, noColorSpace);
    566 
    567     sk_sp<SkSpecialImage> positiveResult1(positiveFilter->filterImage(imgSrc.get(), ctx, &offset));
    568     REPORTER_ASSERT(reporter, positiveResult1);
    569 
    570     sk_sp<SkSpecialImage> negativeResult1(negativeFilter->filterImage(imgSrc.get(), ctx, &offset));
    571     REPORTER_ASSERT(reporter, negativeResult1);
    572 
    573     SkMatrix negativeScale;
    574     negativeScale.setScale(-SK_Scalar1, SK_Scalar1);
    575     SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeWH(32, 32), nullptr,
    576                                        noColorSpace);
    577 
    578     sk_sp<SkSpecialImage> negativeResult2(positiveFilter->filterImage(imgSrc.get(),
    579                                                                       negativeCTX,
    580                                                                       &offset));
    581     REPORTER_ASSERT(reporter, negativeResult2);
    582 
    583     sk_sp<SkSpecialImage> positiveResult2(negativeFilter->filterImage(imgSrc.get(),
    584                                                                       negativeCTX,
    585                                                                       &offset));
    586     REPORTER_ASSERT(reporter, positiveResult2);
    587 
    588 
    589     SkBitmap positiveResultBM1, positiveResultBM2;
    590     SkBitmap negativeResultBM1, negativeResultBM2;
    591 
    592     REPORTER_ASSERT(reporter, positiveResult1->getROPixels(&positiveResultBM1));
    593     REPORTER_ASSERT(reporter, positiveResult2->getROPixels(&positiveResultBM2));
    594     REPORTER_ASSERT(reporter, negativeResult1->getROPixels(&negativeResultBM1));
    595     REPORTER_ASSERT(reporter, negativeResult2->getROPixels(&negativeResultBM2));
    596 
    597     SkAutoLockPixels lockP1(positiveResultBM1);
    598     SkAutoLockPixels lockP2(positiveResultBM2);
    599     SkAutoLockPixels lockN1(negativeResultBM1);
    600     SkAutoLockPixels lockN2(negativeResultBM2);
    601     for (int y = 0; y < height; y++) {
    602         int diffs = memcmp(positiveResultBM1.getAddr32(0, y),
    603                            negativeResultBM1.getAddr32(0, y),
    604                            positiveResultBM1.rowBytes());
    605         REPORTER_ASSERT(reporter, !diffs);
    606         if (diffs) {
    607             break;
    608         }
    609         diffs = memcmp(positiveResultBM1.getAddr32(0, y),
    610                        negativeResultBM2.getAddr32(0, y),
    611                        positiveResultBM1.rowBytes());
    612         REPORTER_ASSERT(reporter, !diffs);
    613         if (diffs) {
    614             break;
    615         }
    616         diffs = memcmp(positiveResultBM1.getAddr32(0, y),
    617                        positiveResultBM2.getAddr32(0, y),
    618                        positiveResultBM1.rowBytes());
    619         REPORTER_ASSERT(reporter, !diffs);
    620         if (diffs) {
    621             break;
    622         }
    623     }
    624 }
    625 
    626 DEF_TEST(ImageFilterNegativeBlurSigma, reporter) {
    627     test_negative_blur_sigma(reporter, nullptr);
    628 }
    629 
    630 #if SK_SUPPORT_GPU
    631 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterNegativeBlurSigma_Gpu, reporter, ctxInfo) {
    632     test_negative_blur_sigma(reporter, ctxInfo.grContext());
    633 }
    634 #endif
    635 
    636 static void test_zero_blur_sigma(skiatest::Reporter* reporter, GrContext* context) {
    637     // Check that SkBlurImageFilter with a zero sigma and a non-zero srcOffset works correctly.
    638     SkImageFilter::CropRect cropRect(SkRect::Make(SkIRect::MakeXYWH(5, 0, 5, 10)));
    639     sk_sp<SkImageFilter> input(SkOffsetImageFilter::Make(0, 0, nullptr, &cropRect));
    640     sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(0, 0, std::move(input), &cropRect));
    641 
    642     sk_sp<SkSpecialSurface> surf(create_empty_special_surface(context, 10));
    643     surf->getCanvas()->clear(SK_ColorGREEN);
    644     sk_sp<SkSpecialImage> image(surf->makeImageSnapshot());
    645 
    646     SkIPoint offset;
    647     SkImageFilter::OutputProperties noColorSpace(nullptr);
    648     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr, noColorSpace);
    649 
    650     sk_sp<SkSpecialImage> result(filter->filterImage(image.get(), ctx, &offset));
    651     REPORTER_ASSERT(reporter, offset.fX == 5 && offset.fY == 0);
    652     REPORTER_ASSERT(reporter, result);
    653     REPORTER_ASSERT(reporter, result->width() == 5 && result->height() == 10);
    654 
    655     SkBitmap resultBM;
    656 
    657     REPORTER_ASSERT(reporter, result->getROPixels(&resultBM));
    658 
    659     SkAutoLockPixels lock(resultBM);
    660     for (int y = 0; y < resultBM.height(); y++) {
    661         for (int x = 0; x < resultBM.width(); x++) {
    662             bool diff = *resultBM.getAddr32(x, y) != SK_ColorGREEN;
    663             REPORTER_ASSERT(reporter, !diff);
    664             if (diff) {
    665                 break;
    666             }
    667         }
    668     }
    669 }
    670 
    671 DEF_TEST(ImageFilterZeroBlurSigma, reporter) {
    672     test_zero_blur_sigma(reporter, nullptr);
    673 }
    674 
    675 #if SK_SUPPORT_GPU
    676 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterZeroBlurSigma_Gpu, reporter, ctxInfo) {
    677     test_zero_blur_sigma(reporter, ctxInfo.grContext());
    678 }
    679 #endif
    680 
    681 
    682 // Tests that, even when an upstream filter has returned null (due to failure or clipping), a
    683 // downstream filter that affects transparent black still does so even with a nullptr input.
    684 static void test_fail_affects_transparent_black(skiatest::Reporter* reporter, GrContext* context) {
    685     sk_sp<FailImageFilter> failFilter(new FailImageFilter());
    686     sk_sp<SkSpecialImage> source(create_empty_special_image(context, 5));
    687     SkImageFilter::OutputProperties noColorSpace(nullptr);
    688     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 1, 1), nullptr, noColorSpace);
    689     sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN, SkBlendMode::kSrc));
    690     SkASSERT(green->affectsTransparentBlack());
    691     sk_sp<SkImageFilter> greenFilter(SkColorFilterImageFilter::Make(std::move(green),
    692                                                                     std::move(failFilter)));
    693     SkIPoint offset;
    694     sk_sp<SkSpecialImage> result(greenFilter->filterImage(source.get(), ctx, &offset));
    695     REPORTER_ASSERT(reporter, nullptr != result.get());
    696     if (result.get()) {
    697         SkBitmap resultBM;
    698         REPORTER_ASSERT(reporter, result->getROPixels(&resultBM));
    699         SkAutoLockPixels lock(resultBM);
    700         REPORTER_ASSERT(reporter, *resultBM.getAddr32(0, 0) == SK_ColorGREEN);
    701     }
    702 }
    703 
    704 DEF_TEST(ImageFilterFailAffectsTransparentBlack, reporter) {
    705     test_fail_affects_transparent_black(reporter, nullptr);
    706 }
    707 
    708 #if SK_SUPPORT_GPU
    709 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterFailAffectsTransparentBlack_Gpu, reporter, ctxInfo) {
    710     test_fail_affects_transparent_black(reporter, ctxInfo.grContext());
    711 }
    712 #endif
    713 
    714 DEF_TEST(ImageFilterDrawTiled, reporter) {
    715     // Check that all filters when drawn tiled (with subsequent clip rects) exactly
    716     // match the same filters drawn with a single full-canvas bitmap draw.
    717     // Tests pass by not asserting.
    718 
    719     FilterList filters(nullptr);
    720 
    721     SkBitmap untiledResult, tiledResult;
    722     const int width = 64, height = 64;
    723     untiledResult.allocN32Pixels(width, height);
    724     tiledResult.allocN32Pixels(width, height);
    725     SkCanvas tiledCanvas(tiledResult);
    726     SkCanvas untiledCanvas(untiledResult);
    727     int tileSize = 8;
    728 
    729     for (int scale = 1; scale <= 2; ++scale) {
    730         for (int i = 0; i < filters.count(); ++i) {
    731             tiledCanvas.clear(0);
    732             untiledCanvas.clear(0);
    733             SkPaint paint;
    734             paint.setImageFilter(sk_ref_sp(filters.getFilter(i)));
    735             paint.setTextSize(SkIntToScalar(height));
    736             paint.setColor(SK_ColorWHITE);
    737             SkString str;
    738             const char* text = "ABC";
    739             SkScalar ypos = SkIntToScalar(height);
    740             untiledCanvas.save();
    741             untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
    742             untiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
    743             untiledCanvas.restore();
    744             for (int y = 0; y < height; y += tileSize) {
    745                 for (int x = 0; x < width; x += tileSize) {
    746                     tiledCanvas.save();
    747                     tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
    748                     tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
    749                     tiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
    750                     tiledCanvas.restore();
    751                 }
    752             }
    753             untiledCanvas.flush();
    754             tiledCanvas.flush();
    755             for (int y = 0; y < height; y++) {
    756                 int diffs = memcmp(untiledResult.getAddr32(0, y), tiledResult.getAddr32(0, y), untiledResult.rowBytes());
    757                 REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters.getName(i));
    758                 if (diffs) {
    759                     break;
    760                 }
    761             }
    762         }
    763     }
    764 }
    765 
    766 static void draw_saveLayer_picture(int width, int height, int tileSize,
    767                                    SkBBHFactory* factory, SkBitmap* result) {
    768 
    769     SkMatrix matrix;
    770     matrix.setTranslate(SkIntToScalar(50), 0);
    771 
    772     sk_sp<SkColorFilter> cf(SkColorFilter::MakeModeFilter(SK_ColorWHITE, SkBlendMode::kSrc));
    773     sk_sp<SkImageFilter> cfif(SkColorFilterImageFilter::Make(std::move(cf), nullptr));
    774     sk_sp<SkImageFilter> imageFilter(SkImageFilter::MakeMatrixFilter(matrix,
    775                                                                      kNone_SkFilterQuality,
    776                                                                      std::move(cfif)));
    777 
    778     SkPaint paint;
    779     paint.setImageFilter(std::move(imageFilter));
    780     SkPictureRecorder recorder;
    781     SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50));
    782     SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
    783                                                         SkIntToScalar(height),
    784                                                         factory, 0);
    785     recordingCanvas->translate(-55, 0);
    786     recordingCanvas->saveLayer(&bounds, &paint);
    787     recordingCanvas->restore();
    788     sk_sp<SkPicture> picture1(recorder.finishRecordingAsPicture());
    789 
    790     result->allocN32Pixels(width, height);
    791     SkCanvas canvas(*result);
    792     canvas.clear(0);
    793     canvas.clipRect(SkRect::Make(SkIRect::MakeWH(tileSize, tileSize)));
    794     canvas.drawPicture(picture1.get());
    795 }
    796 
    797 DEF_TEST(ImageFilterDrawMatrixBBH, reporter) {
    798     // Check that matrix filter when drawn tiled with BBH exactly
    799     // matches the same thing drawn without BBH.
    800     // Tests pass by not asserting.
    801 
    802     const int width = 200, height = 200;
    803     const int tileSize = 100;
    804     SkBitmap result1, result2;
    805     SkRTreeFactory factory;
    806 
    807     draw_saveLayer_picture(width, height, tileSize, &factory, &result1);
    808     draw_saveLayer_picture(width, height, tileSize, nullptr, &result2);
    809 
    810     for (int y = 0; y < height; y++) {
    811         int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
    812         REPORTER_ASSERT(reporter, !diffs);
    813         if (diffs) {
    814             break;
    815         }
    816     }
    817 }
    818 
    819 static sk_sp<SkImageFilter> make_blur(sk_sp<SkImageFilter> input) {
    820     return SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1, std::move(input));
    821 }
    822 
    823 static sk_sp<SkImageFilter> make_drop_shadow(sk_sp<SkImageFilter> input) {
    824     return SkDropShadowImageFilter::Make(
    825         SkIntToScalar(100), SkIntToScalar(100),
    826         SkIntToScalar(10), SkIntToScalar(10),
    827         SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
    828         std::move(input));
    829 }
    830 
    831 DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
    832     sk_sp<SkImageFilter> filter1(make_blur(nullptr));
    833     sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1)));
    834 
    835     SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
    836     SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
    837     bounds = filter2->filterBounds(bounds, SkMatrix::I());
    838 
    839     REPORTER_ASSERT(reporter, bounds == expectedBounds);
    840 }
    841 
    842 DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
    843     sk_sp<SkImageFilter> filter1(make_drop_shadow(nullptr));
    844     sk_sp<SkImageFilter> filter2(make_blur(std::move(filter1)));
    845 
    846     SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
    847     SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
    848     bounds = filter2->filterBounds(bounds, SkMatrix::I());
    849 
    850     REPORTER_ASSERT(reporter, bounds == expectedBounds);
    851 }
    852 
    853 DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
    854     sk_sp<SkImageFilter> filter1(SkDilateImageFilter::Make(2, 2, nullptr));
    855     sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1)));
    856 
    857     SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
    858     SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234);
    859     bounds = filter2->filterBounds(bounds, SkMatrix::I());
    860 
    861     REPORTER_ASSERT(reporter, bounds == expectedBounds);
    862 }
    863 
    864 DEF_TEST(ImageFilterScaledBlurRadius, reporter) {
    865     // Each blur should spread 3*sigma, so 3 for the blur and 30 for the shadow
    866     // (before the CTM). Bounds should be computed correctly in the presence of
    867     // a (possibly negative) scale.
    868     sk_sp<SkImageFilter> blur(make_blur(nullptr));
    869     sk_sp<SkImageFilter> dropShadow(make_drop_shadow(nullptr));
    870     {
    871         // Uniform scale by 2.
    872         SkMatrix scaleMatrix;
    873         scaleMatrix.setScale(2, 2);
    874         SkIRect bounds = SkIRect::MakeLTRB(0, 0, 200, 200);
    875 
    876         SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-6, -6, 206, 206);
    877         SkIRect blurBounds = blur->filterBounds(
    878             bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
    879         REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds);
    880         SkIRect reverseBlurBounds = blur->filterBounds(
    881             bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
    882         REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds);
    883 
    884         SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, 0, 460, 460);
    885         SkIRect shadowBounds = dropShadow->filterBounds(
    886             bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
    887         REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds);
    888         SkIRect expectedReverseShadowBounds =
    889             SkIRect::MakeLTRB(-260, -260, 200, 200);
    890         SkIRect reverseShadowBounds = dropShadow->filterBounds(
    891             bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
    892         REPORTER_ASSERT(reporter,
    893             reverseShadowBounds == expectedReverseShadowBounds);
    894     }
    895     {
    896         // Vertical flip.
    897         SkMatrix scaleMatrix;
    898         scaleMatrix.setScale(1, -1);
    899         SkIRect bounds = SkIRect::MakeLTRB(0, -100, 100, 0);
    900 
    901         SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-3, -103, 103, 3);
    902         SkIRect blurBounds = blur->filterBounds(
    903             bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
    904         REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds);
    905         SkIRect reverseBlurBounds = blur->filterBounds(
    906             bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
    907         REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds);
    908 
    909         SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, -230, 230, 0);
    910         SkIRect shadowBounds = dropShadow->filterBounds(
    911             bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
    912         REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds);
    913         SkIRect expectedReverseShadowBounds =
    914             SkIRect::MakeLTRB(-130, -100, 100, 130);
    915         SkIRect reverseShadowBounds = dropShadow->filterBounds(
    916             bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
    917         REPORTER_ASSERT(reporter,
    918             reverseShadowBounds == expectedReverseShadowBounds);
    919     }
    920 }
    921 
    922 DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
    923     sk_sp<SkImageFilter> filter1(make_blur(nullptr));
    924     sk_sp<SkImageFilter> filter2(make_blur(nullptr));
    925     sk_sp<SkImageFilter> composedFilter(SkComposeImageFilter::Make(std::move(filter1),
    926                                                                    std::move(filter2)));
    927 
    928     SkRect boundsSrc = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
    929     SkRect expectedBounds = SkRect::MakeXYWH(
    930         SkIntToScalar(-6), SkIntToScalar(-6), SkIntToScalar(112), SkIntToScalar(112));
    931     SkRect boundsDst = composedFilter->computeFastBounds(boundsSrc);
    932 
    933     REPORTER_ASSERT(reporter, boundsDst == expectedBounds);
    934 }
    935 
    936 DEF_TEST(ImageFilterUnionBounds, reporter) {
    937     sk_sp<SkImageFilter> offset(SkOffsetImageFilter::Make(50, 0, nullptr));
    938     // Regardless of which order they appear in, the image filter bounds should
    939     // be combined correctly.
    940     {
    941         sk_sp<SkImageFilter> composite(SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver, offset));
    942         SkRect bounds = SkRect::MakeWH(100, 100);
    943         // Intentionally aliasing here, as that's what the real callers do.
    944         bounds = composite->computeFastBounds(bounds);
    945         REPORTER_ASSERT(reporter, bounds == SkRect::MakeWH(150, 100));
    946     }
    947     {
    948         sk_sp<SkImageFilter> composite(SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver, nullptr,
    949                                                                    offset, nullptr));
    950         SkRect bounds = SkRect::MakeWH(100, 100);
    951         // Intentionally aliasing here, as that's what the real callers do.
    952         bounds = composite->computeFastBounds(bounds);
    953         REPORTER_ASSERT(reporter, bounds == SkRect::MakeWH(150, 100));
    954     }
    955 }
    956 
    957 static void test_imagefilter_merge_result_size(skiatest::Reporter* reporter, GrContext* context) {
    958     SkBitmap greenBM;
    959     greenBM.allocN32Pixels(20, 20);
    960     greenBM.eraseColor(SK_ColorGREEN);
    961     sk_sp<SkImage> greenImage(SkImage::MakeFromBitmap(greenBM));
    962     sk_sp<SkImageFilter> source(SkImageSource::Make(std::move(greenImage)));
    963     sk_sp<SkImageFilter> merge(SkMergeImageFilter::Make(source, source, SkBlendMode::kSrcOver));
    964 
    965     sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 1));
    966 
    967     SkImageFilter::OutputProperties noColorSpace(nullptr);
    968     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 100, 100), nullptr,
    969                                noColorSpace);
    970     SkIPoint offset;
    971 
    972     sk_sp<SkSpecialImage> resultImg(merge->filterImage(srcImg.get(), ctx, &offset));
    973     REPORTER_ASSERT(reporter, resultImg);
    974 
    975     REPORTER_ASSERT(reporter, resultImg->width() == 20 && resultImg->height() == 20);
    976 }
    977 
    978 DEF_TEST(ImageFilterMergeResultSize, reporter) {
    979     test_imagefilter_merge_result_size(reporter, nullptr);
    980 }
    981 
    982 #if SK_SUPPORT_GPU
    983 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMergeResultSize_Gpu, reporter, ctxInfo) {
    984     test_imagefilter_merge_result_size(reporter, ctxInfo.grContext());
    985 }
    986 #endif
    987 
    988 static void draw_blurred_rect(SkCanvas* canvas) {
    989     SkPaint filterPaint;
    990     filterPaint.setColor(SK_ColorWHITE);
    991     filterPaint.setImageFilter(SkBlurImageFilter::Make(SkIntToScalar(8), 0, nullptr));
    992     canvas->saveLayer(nullptr, &filterPaint);
    993     SkPaint whitePaint;
    994     whitePaint.setColor(SK_ColorWHITE);
    995     canvas->drawRect(SkRect::Make(SkIRect::MakeWH(4, 4)), whitePaint);
    996     canvas->restore();
    997 }
    998 
    999 static void draw_picture_clipped(SkCanvas* canvas, const SkRect& clipRect, const SkPicture* picture) {
   1000     canvas->save();
   1001     canvas->clipRect(clipRect);
   1002     canvas->drawPicture(picture);
   1003     canvas->restore();
   1004 }
   1005 
   1006 DEF_TEST(ImageFilterDrawTiledBlurRTree, reporter) {
   1007     // Check that the blur filter when recorded with RTree acceleration,
   1008     // and drawn tiled (with subsequent clip rects) exactly
   1009     // matches the same filter drawn with without RTree acceleration.
   1010     // This tests that the "bleed" from the blur into the otherwise-blank
   1011     // tiles is correctly rendered.
   1012     // Tests pass by not asserting.
   1013 
   1014     int width = 16, height = 8;
   1015     SkBitmap result1, result2;
   1016     result1.allocN32Pixels(width, height);
   1017     result2.allocN32Pixels(width, height);
   1018     SkCanvas canvas1(result1);
   1019     SkCanvas canvas2(result2);
   1020     int tileSize = 8;
   1021 
   1022     canvas1.clear(0);
   1023     canvas2.clear(0);
   1024 
   1025     SkRTreeFactory factory;
   1026 
   1027     SkPictureRecorder recorder1, recorder2;
   1028     // The only difference between these two pictures is that one has RTree aceleration.
   1029     SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width),
   1030                                                           SkIntToScalar(height),
   1031                                                           nullptr, 0);
   1032     SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width),
   1033                                                           SkIntToScalar(height),
   1034                                                           &factory, 0);
   1035     draw_blurred_rect(recordingCanvas1);
   1036     draw_blurred_rect(recordingCanvas2);
   1037     sk_sp<SkPicture> picture1(recorder1.finishRecordingAsPicture());
   1038     sk_sp<SkPicture> picture2(recorder2.finishRecordingAsPicture());
   1039     for (int y = 0; y < height; y += tileSize) {
   1040         for (int x = 0; x < width; x += tileSize) {
   1041             SkRect tileRect = SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize));
   1042             draw_picture_clipped(&canvas1, tileRect, picture1.get());
   1043             draw_picture_clipped(&canvas2, tileRect, picture2.get());
   1044         }
   1045     }
   1046     for (int y = 0; y < height; y++) {
   1047         int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
   1048         REPORTER_ASSERT(reporter, !diffs);
   1049         if (diffs) {
   1050             break;
   1051         }
   1052     }
   1053 }
   1054 
   1055 DEF_TEST(ImageFilterMatrixConvolution, reporter) {
   1056     // Check that a 1x3 filter does not cause a spurious assert.
   1057     SkScalar kernel[3] = {
   1058         SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
   1059     };
   1060     SkISize kernelSize = SkISize::Make(1, 3);
   1061     SkScalar gain = SK_Scalar1, bias = 0;
   1062     SkIPoint kernelOffset = SkIPoint::Make(0, 0);
   1063 
   1064     sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make(
   1065                                             kernelSize, kernel,
   1066                                             gain, bias, kernelOffset,
   1067                                             SkMatrixConvolutionImageFilter::kRepeat_TileMode,
   1068                                             false, nullptr));
   1069 
   1070     SkBitmap result;
   1071     int width = 16, height = 16;
   1072     result.allocN32Pixels(width, height);
   1073     SkCanvas canvas(result);
   1074     canvas.clear(0);
   1075 
   1076     SkPaint paint;
   1077     paint.setImageFilter(std::move(filter));
   1078     SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
   1079     canvas.drawRect(rect, paint);
   1080 }
   1081 
   1082 DEF_TEST(ImageFilterMatrixConvolutionBorder, reporter) {
   1083     // Check that a filter with borders outside the target bounds
   1084     // does not crash.
   1085     SkScalar kernel[3] = {
   1086         0, 0, 0,
   1087     };
   1088     SkISize kernelSize = SkISize::Make(3, 1);
   1089     SkScalar gain = SK_Scalar1, bias = 0;
   1090     SkIPoint kernelOffset = SkIPoint::Make(2, 0);
   1091 
   1092     sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make(
   1093                                             kernelSize, kernel, gain, bias, kernelOffset,
   1094                                             SkMatrixConvolutionImageFilter::kClamp_TileMode,
   1095                                             true, nullptr));
   1096 
   1097     SkBitmap result;
   1098 
   1099     int width = 10, height = 10;
   1100     result.allocN32Pixels(width, height);
   1101     SkCanvas canvas(result);
   1102     canvas.clear(0);
   1103 
   1104     SkPaint filterPaint;
   1105     filterPaint.setImageFilter(std::move(filter));
   1106     SkRect bounds = SkRect::MakeWH(1, 10);
   1107     SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
   1108     SkPaint rectPaint;
   1109     canvas.saveLayer(&bounds, &filterPaint);
   1110     canvas.drawRect(rect, rectPaint);
   1111     canvas.restore();
   1112 }
   1113 
   1114 static void test_big_kernel(skiatest::Reporter* reporter, GrContext* context) {
   1115     // Check that a kernel that is too big for the GPU still works
   1116     SkScalar identityKernel[49] = {
   1117         0, 0, 0, 0, 0, 0, 0,
   1118         0, 0, 0, 0, 0, 0, 0,
   1119         0, 0, 0, 0, 0, 0, 0,
   1120         0, 0, 0, 1, 0, 0, 0,
   1121         0, 0, 0, 0, 0, 0, 0,
   1122         0, 0, 0, 0, 0, 0, 0,
   1123         0, 0, 0, 0, 0, 0, 0
   1124     };
   1125     SkISize kernelSize = SkISize::Make(7, 7);
   1126     SkScalar gain = SK_Scalar1, bias = 0;
   1127     SkIPoint kernelOffset = SkIPoint::Make(0, 0);
   1128 
   1129     sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make(
   1130                                         kernelSize, identityKernel, gain, bias, kernelOffset,
   1131                                         SkMatrixConvolutionImageFilter::kClamp_TileMode,
   1132                                         true, nullptr));
   1133 
   1134     sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100));
   1135     SkASSERT(srcImg);
   1136 
   1137     SkIPoint offset;
   1138     SkImageFilter::OutputProperties noColorSpace(nullptr);
   1139     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
   1140     sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
   1141     REPORTER_ASSERT(reporter, resultImg);
   1142     REPORTER_ASSERT(reporter, SkToBool(context) == resultImg->isTextureBacked());
   1143     REPORTER_ASSERT(reporter, resultImg->width() == 100 && resultImg->height() == 100);
   1144     REPORTER_ASSERT(reporter, offset.fX == 0 && offset.fY == 0);
   1145 }
   1146 
   1147 DEF_TEST(ImageFilterMatrixConvolutionBigKernel, reporter) {
   1148     test_big_kernel(reporter, nullptr);
   1149 }
   1150 
   1151 #if SK_SUPPORT_GPU
   1152 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMatrixConvolutionBigKernel_Gpu,
   1153                                    reporter, ctxInfo) {
   1154     test_big_kernel(reporter, ctxInfo.grContext());
   1155 }
   1156 #endif
   1157 
   1158 DEF_TEST(ImageFilterCropRect, reporter) {
   1159     test_crop_rects(reporter, nullptr);
   1160 }
   1161 
   1162 #if SK_SUPPORT_GPU
   1163 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCropRect_Gpu, reporter, ctxInfo) {
   1164     test_crop_rects(reporter, ctxInfo.grContext());
   1165 }
   1166 #endif
   1167 
   1168 DEF_TEST(ImageFilterMatrix, reporter) {
   1169     SkBitmap temp;
   1170     temp.allocN32Pixels(100, 100);
   1171     SkCanvas canvas(temp);
   1172     canvas.scale(SkIntToScalar(2), SkIntToScalar(2));
   1173 
   1174     SkMatrix expectedMatrix = canvas.getTotalMatrix();
   1175 
   1176     SkRTreeFactory factory;
   1177     SkPictureRecorder recorder;
   1178     SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory, 0);
   1179 
   1180     SkPaint paint;
   1181     paint.setImageFilter(MatrixTestImageFilter::Make(reporter, expectedMatrix));
   1182     recordingCanvas->saveLayer(nullptr, &paint);
   1183     SkPaint solidPaint;
   1184     solidPaint.setColor(0xFFFFFFFF);
   1185     recordingCanvas->save();
   1186     recordingCanvas->scale(SkIntToScalar(10), SkIntToScalar(10));
   1187     recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint);
   1188     recordingCanvas->restore(); // scale
   1189     recordingCanvas->restore(); // saveLayer
   1190 
   1191     canvas.drawPicture(recorder.finishRecordingAsPicture());
   1192 }
   1193 
   1194 DEF_TEST(ImageFilterCrossProcessPictureImageFilter, reporter) {
   1195     SkRTreeFactory factory;
   1196     SkPictureRecorder recorder;
   1197     SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
   1198 
   1199     // Create an SkPicture which simply draws a green 1x1 rectangle.
   1200     SkPaint greenPaint;
   1201     greenPaint.setColor(SK_ColorGREEN);
   1202     recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
   1203     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
   1204 
   1205     // Wrap that SkPicture in an SkPictureImageFilter.
   1206     sk_sp<SkImageFilter> imageFilter(SkPictureImageFilter::Make(picture));
   1207 
   1208     // Check that SkPictureImageFilter successfully serializes its contained
   1209     // SkPicture when not in cross-process mode.
   1210     SkPaint paint;
   1211     paint.setImageFilter(imageFilter);
   1212     SkPictureRecorder outerRecorder;
   1213     SkCanvas* outerCanvas = outerRecorder.beginRecording(1, 1, &factory, 0);
   1214     SkPaint redPaintWithFilter;
   1215     redPaintWithFilter.setColor(SK_ColorRED);
   1216     redPaintWithFilter.setImageFilter(imageFilter);
   1217     outerCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
   1218     sk_sp<SkPicture> outerPicture(outerRecorder.finishRecordingAsPicture());
   1219 
   1220     SkBitmap bitmap;
   1221     bitmap.allocN32Pixels(1, 1);
   1222     SkCanvas canvas(bitmap);
   1223 
   1224     // The result here should be green, since the filter replaces the primitive's red interior.
   1225     canvas.clear(0x0);
   1226     canvas.drawPicture(outerPicture);
   1227     uint32_t pixel = *bitmap.getAddr32(0, 0);
   1228     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
   1229 
   1230     // Check that, for now, SkPictureImageFilter does not serialize or
   1231     // deserialize its contained picture when the filter is serialized
   1232     // cross-process. Do this by "laundering" it through SkValidatingReadBuffer.
   1233     sk_sp<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get()));
   1234     sk_sp<SkImageFilter> unflattenedFilter = SkValidatingDeserializeImageFilter(data->data(),
   1235                                                                                 data->size());
   1236 
   1237     redPaintWithFilter.setImageFilter(unflattenedFilter);
   1238     SkPictureRecorder crossProcessRecorder;
   1239     SkCanvas* crossProcessCanvas = crossProcessRecorder.beginRecording(1, 1, &factory, 0);
   1240     crossProcessCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
   1241     sk_sp<SkPicture> crossProcessPicture(crossProcessRecorder.finishRecordingAsPicture());
   1242 
   1243     canvas.clear(0x0);
   1244     canvas.drawPicture(crossProcessPicture);
   1245     pixel = *bitmap.getAddr32(0, 0);
   1246     // If the security precautions are enabled, the result here should not be green, since the
   1247     // filter draws nothing.
   1248     REPORTER_ASSERT(reporter, SkPicture::PictureIOSecurityPrecautionsEnabled()
   1249         ? pixel != SK_ColorGREEN : pixel == SK_ColorGREEN);
   1250 }
   1251 
   1252 static void test_clipped_picture_imagefilter(skiatest::Reporter* reporter, GrContext* context) {
   1253     sk_sp<SkPicture> picture;
   1254 
   1255     {
   1256         SkRTreeFactory factory;
   1257         SkPictureRecorder recorder;
   1258         SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
   1259 
   1260         // Create an SkPicture which simply draws a green 1x1 rectangle.
   1261         SkPaint greenPaint;
   1262         greenPaint.setColor(SK_ColorGREEN);
   1263         recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
   1264         picture = recorder.finishRecordingAsPicture();
   1265     }
   1266 
   1267     sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 2));
   1268 
   1269     sk_sp<SkImageFilter> imageFilter(SkPictureImageFilter::Make(picture));
   1270 
   1271     SkIPoint offset;
   1272     SkImageFilter::OutputProperties noColorSpace(nullptr);
   1273     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), nullptr, noColorSpace);
   1274 
   1275     sk_sp<SkSpecialImage> resultImage(imageFilter->filterImage(srcImg.get(), ctx, &offset));
   1276     REPORTER_ASSERT(reporter, !resultImage);
   1277 }
   1278 
   1279 DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) {
   1280     test_clipped_picture_imagefilter(reporter, nullptr);
   1281 }
   1282 
   1283 #if SK_SUPPORT_GPU
   1284 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterClippedPictureImageFilter_Gpu, reporter, ctxInfo) {
   1285     test_clipped_picture_imagefilter(reporter, ctxInfo.grContext());
   1286 }
   1287 #endif
   1288 
   1289 DEF_TEST(ImageFilterEmptySaveLayer, reporter) {
   1290     // Even when there's an empty saveLayer()/restore(), ensure that an image
   1291     // filter or color filter which affects transparent black still draws.
   1292 
   1293     SkBitmap bitmap;
   1294     bitmap.allocN32Pixels(10, 10);
   1295     SkCanvas canvas(bitmap);
   1296 
   1297     SkRTreeFactory factory;
   1298     SkPictureRecorder recorder;
   1299 
   1300     sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN,
   1301                                                              SkBlendMode::kSrc));
   1302     sk_sp<SkImageFilter> imageFilter(SkColorFilterImageFilter::Make(green, nullptr));
   1303     SkPaint imageFilterPaint;
   1304     imageFilterPaint.setImageFilter(std::move(imageFilter));
   1305     SkPaint colorFilterPaint;
   1306     colorFilterPaint.setColorFilter(green);
   1307 
   1308     SkRect bounds = SkRect::MakeWH(10, 10);
   1309 
   1310     SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
   1311     recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
   1312     recordingCanvas->restore();
   1313     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
   1314 
   1315     canvas.clear(0);
   1316     canvas.drawPicture(picture);
   1317     uint32_t pixel = *bitmap.getAddr32(0, 0);
   1318     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
   1319 
   1320     recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
   1321     recordingCanvas->saveLayer(nullptr, &imageFilterPaint);
   1322     recordingCanvas->restore();
   1323     sk_sp<SkPicture> picture2(recorder.finishRecordingAsPicture());
   1324 
   1325     canvas.clear(0);
   1326     canvas.drawPicture(picture2);
   1327     pixel = *bitmap.getAddr32(0, 0);
   1328     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
   1329 
   1330     recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
   1331     recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
   1332     recordingCanvas->restore();
   1333     sk_sp<SkPicture> picture3(recorder.finishRecordingAsPicture());
   1334 
   1335     canvas.clear(0);
   1336     canvas.drawPicture(picture3);
   1337     pixel = *bitmap.getAddr32(0, 0);
   1338     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
   1339 }
   1340 
   1341 static void test_huge_blur(SkCanvas* canvas, skiatest::Reporter* reporter) {
   1342     SkBitmap bitmap;
   1343     bitmap.allocN32Pixels(100, 100);
   1344     bitmap.eraseARGB(0, 0, 0, 0);
   1345 
   1346     // Check that a blur with an insane radius does not crash or assert.
   1347     SkPaint paint;
   1348     paint.setImageFilter(SkBlurImageFilter::Make(SkIntToScalar(1<<30),
   1349                                                  SkIntToScalar(1<<30),
   1350                                                  nullptr));
   1351     canvas->drawBitmap(bitmap, 0, 0, &paint);
   1352 }
   1353 
   1354 DEF_TEST(HugeBlurImageFilter, reporter) {
   1355     SkBitmap temp;
   1356     temp.allocN32Pixels(100, 100);
   1357     SkCanvas canvas(temp);
   1358     test_huge_blur(&canvas, reporter);
   1359 }
   1360 
   1361 DEF_TEST(ImageFilterMatrixConvolutionSanityTest, reporter) {
   1362     SkScalar kernel[1] = { 0 };
   1363     SkScalar gain = SK_Scalar1, bias = 0;
   1364     SkIPoint kernelOffset = SkIPoint::Make(1, 1);
   1365 
   1366     // Check that an enormous (non-allocatable) kernel gives a nullptr filter.
   1367     sk_sp<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Make(
   1368         SkISize::Make(1<<30, 1<<30),
   1369         kernel,
   1370         gain,
   1371         bias,
   1372         kernelOffset,
   1373         SkMatrixConvolutionImageFilter::kRepeat_TileMode,
   1374         false,
   1375         nullptr));
   1376 
   1377     REPORTER_ASSERT(reporter, nullptr == conv.get());
   1378 
   1379     // Check that a nullptr kernel gives a nullptr filter.
   1380     conv = SkMatrixConvolutionImageFilter::Make(
   1381         SkISize::Make(1, 1),
   1382         nullptr,
   1383         gain,
   1384         bias,
   1385         kernelOffset,
   1386         SkMatrixConvolutionImageFilter::kRepeat_TileMode,
   1387         false,
   1388         nullptr);
   1389 
   1390     REPORTER_ASSERT(reporter, nullptr == conv.get());
   1391 
   1392     // Check that a kernel width < 1 gives a nullptr filter.
   1393     conv = SkMatrixConvolutionImageFilter::Make(
   1394         SkISize::Make(0, 1),
   1395         kernel,
   1396         gain,
   1397         bias,
   1398         kernelOffset,
   1399         SkMatrixConvolutionImageFilter::kRepeat_TileMode,
   1400         false,
   1401         nullptr);
   1402 
   1403     REPORTER_ASSERT(reporter, nullptr == conv.get());
   1404 
   1405     // Check that kernel height < 1 gives a nullptr filter.
   1406     conv = SkMatrixConvolutionImageFilter::Make(
   1407         SkISize::Make(1, -1),
   1408         kernel,
   1409         gain,
   1410         bias,
   1411         kernelOffset,
   1412         SkMatrixConvolutionImageFilter::kRepeat_TileMode,
   1413         false,
   1414         nullptr);
   1415 
   1416     REPORTER_ASSERT(reporter, nullptr == conv.get());
   1417 }
   1418 
   1419 static void test_xfermode_cropped_input(SkCanvas* canvas, skiatest::Reporter* reporter) {
   1420     canvas->clear(0);
   1421 
   1422     SkBitmap bitmap;
   1423     bitmap.allocN32Pixels(1, 1);
   1424     bitmap.eraseARGB(255, 255, 255, 255);
   1425 
   1426     sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN,
   1427                                                              SkBlendMode::kSrcIn));
   1428     sk_sp<SkImageFilter> greenFilter(SkColorFilterImageFilter::Make(green, nullptr));
   1429     SkImageFilter::CropRect cropRect(SkRect::MakeEmpty());
   1430     sk_sp<SkImageFilter> croppedOut(SkColorFilterImageFilter::Make(green, nullptr, &cropRect));
   1431 
   1432     // Check that an xfermode image filter whose input has been cropped out still draws the other
   1433     // input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
   1434     SkBlendMode mode = SkBlendMode::kSrcOver;
   1435     sk_sp<SkImageFilter> xfermodeNoFg(SkXfermodeImageFilter::Make(mode, greenFilter,
   1436                                                                   croppedOut, nullptr));
   1437     sk_sp<SkImageFilter> xfermodeNoBg(SkXfermodeImageFilter::Make(mode, croppedOut,
   1438                                                                   greenFilter, nullptr));
   1439     sk_sp<SkImageFilter> xfermodeNoFgNoBg(SkXfermodeImageFilter::Make(mode, croppedOut,
   1440                                                                       croppedOut, nullptr));
   1441 
   1442     SkPaint paint;
   1443     paint.setImageFilter(std::move(xfermodeNoFg));
   1444     canvas->drawBitmap(bitmap, 0, 0, &paint);   // drawSprite
   1445 
   1446     uint32_t pixel;
   1447     SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
   1448     canvas->readPixels(info, &pixel, 4, 0, 0);
   1449     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
   1450 
   1451     paint.setImageFilter(std::move(xfermodeNoBg));
   1452     canvas->drawBitmap(bitmap, 0, 0, &paint);   // drawSprite
   1453     canvas->readPixels(info, &pixel, 4, 0, 0);
   1454     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
   1455 
   1456     paint.setImageFilter(std::move(xfermodeNoFgNoBg));
   1457     canvas->drawBitmap(bitmap, 0, 0, &paint);   // drawSprite
   1458     canvas->readPixels(info, &pixel, 4, 0, 0);
   1459     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
   1460 }
   1461 
   1462 DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
   1463     SkBitmap temp;
   1464     temp.allocN32Pixels(50, 50);
   1465     SkCanvas canvas(temp);
   1466     canvas.clear(0x0);
   1467 
   1468     SkBitmap bitmap;
   1469     bitmap.allocN32Pixels(10, 10);
   1470     bitmap.eraseColor(SK_ColorGREEN);
   1471 
   1472     SkMatrix matrix;
   1473     matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
   1474     matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
   1475     sk_sp<SkImageFilter> matrixFilter(
   1476         SkImageFilter::MakeMatrixFilter(matrix, kLow_SkFilterQuality, nullptr));
   1477 
   1478     // Test that saveLayer() with a filter nested inside another saveLayer() applies the
   1479     // correct offset to the filter matrix.
   1480     SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
   1481     canvas.saveLayer(&bounds1, nullptr);
   1482     SkPaint filterPaint;
   1483     filterPaint.setImageFilter(std::move(matrixFilter));
   1484     SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
   1485     canvas.saveLayer(&bounds2, &filterPaint);
   1486     SkPaint greenPaint;
   1487     greenPaint.setColor(SK_ColorGREEN);
   1488     canvas.drawRect(bounds2, greenPaint);
   1489     canvas.restore();
   1490     canvas.restore();
   1491     SkPaint strokePaint;
   1492     strokePaint.setStyle(SkPaint::kStroke_Style);
   1493     strokePaint.setColor(SK_ColorRED);
   1494 
   1495     SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
   1496     uint32_t pixel;
   1497     canvas.readPixels(info, &pixel, 4, 25, 25);
   1498     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
   1499 
   1500     // Test that drawSprite() with a filter nested inside a saveLayer() applies the
   1501     // correct offset to the filter matrix.
   1502     canvas.clear(0x0);
   1503     canvas.readPixels(info, &pixel, 4, 25, 25);
   1504     canvas.saveLayer(&bounds1, nullptr);
   1505     canvas.drawBitmap(bitmap, 20, 20, &filterPaint);    // drawSprite
   1506     canvas.restore();
   1507 
   1508     canvas.readPixels(info, &pixel, 4, 25, 25);
   1509     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
   1510 }
   1511 
   1512 DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
   1513     SkBitmap temp;
   1514     temp.allocN32Pixels(100, 100);
   1515     SkCanvas canvas(temp);
   1516     test_xfermode_cropped_input(&canvas, reporter);
   1517 }
   1518 
   1519 static void test_composed_imagefilter_offset(skiatest::Reporter* reporter, GrContext* context) {
   1520     sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100));
   1521 
   1522     SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
   1523     sk_sp<SkImageFilter> offsetFilter(SkOffsetImageFilter::Make(0, 0, nullptr, &cropRect));
   1524     sk_sp<SkImageFilter> blurFilter(SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1,
   1525                                                             nullptr, &cropRect));
   1526     sk_sp<SkImageFilter> composedFilter(SkComposeImageFilter::Make(std::move(blurFilter),
   1527                                                                    std::move(offsetFilter)));
   1528     SkIPoint offset;
   1529     SkImageFilter::OutputProperties noColorSpace(nullptr);
   1530     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
   1531 
   1532     sk_sp<SkSpecialImage> resultImg(composedFilter->filterImage(srcImg.get(), ctx, &offset));
   1533     REPORTER_ASSERT(reporter, resultImg);
   1534     REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0);
   1535 }
   1536 
   1537 DEF_TEST(ComposedImageFilterOffset, reporter) {
   1538     test_composed_imagefilter_offset(reporter, nullptr);
   1539 }
   1540 
   1541 #if SK_SUPPORT_GPU
   1542 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterOffset_Gpu, reporter, ctxInfo) {
   1543     test_composed_imagefilter_offset(reporter, ctxInfo.grContext());
   1544 }
   1545 #endif
   1546 
   1547 static void test_composed_imagefilter_bounds(skiatest::Reporter* reporter, GrContext* context) {
   1548     // The bounds passed to the inner filter must be filtered by the outer
   1549     // filter, so that the inner filter produces the pixels that the outer
   1550     // filter requires as input. This matters if the outer filter moves pixels.
   1551     // Here, accounting for the outer offset is necessary so that the green
   1552     // pixels of the picture are not clipped.
   1553 
   1554     SkPictureRecorder recorder;
   1555     SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeWH(200, 100));
   1556     recordingCanvas->clipRect(SkRect::MakeXYWH(100, 0, 100, 100));
   1557     recordingCanvas->clear(SK_ColorGREEN);
   1558     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
   1559     sk_sp<SkImageFilter> pictureFilter(SkPictureImageFilter::Make(picture));
   1560     SkImageFilter::CropRect cropRect(SkRect::MakeWH(100, 100));
   1561     sk_sp<SkImageFilter> offsetFilter(SkOffsetImageFilter::Make(-100, 0, nullptr, &cropRect));
   1562     sk_sp<SkImageFilter> composedFilter(SkComposeImageFilter::Make(std::move(offsetFilter),
   1563                                                                    std::move(pictureFilter)));
   1564 
   1565     sk_sp<SkSpecialImage> sourceImage(create_empty_special_image(context, 100));
   1566     SkImageFilter::OutputProperties noColorSpace(nullptr);
   1567     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
   1568     SkIPoint offset;
   1569     sk_sp<SkSpecialImage> result(composedFilter->filterImage(sourceImage.get(), ctx, &offset));
   1570     REPORTER_ASSERT(reporter, offset.isZero());
   1571     REPORTER_ASSERT(reporter, result);
   1572     REPORTER_ASSERT(reporter, result->subset().size() == SkISize::Make(100, 100));
   1573 
   1574     SkBitmap resultBM;
   1575     REPORTER_ASSERT(reporter, result->getROPixels(&resultBM));
   1576     SkAutoLockPixels lock(resultBM);
   1577     REPORTER_ASSERT(reporter, resultBM.getColor(50, 50) == SK_ColorGREEN);
   1578 }
   1579 
   1580 DEF_TEST(ComposedImageFilterBounds, reporter) {
   1581     test_composed_imagefilter_bounds(reporter, nullptr);
   1582 }
   1583 
   1584 #if SK_SUPPORT_GPU
   1585 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterBounds_Gpu, reporter, ctxInfo) {
   1586     test_composed_imagefilter_bounds(reporter, ctxInfo.grContext());
   1587 }
   1588 #endif
   1589 
   1590 static void test_partial_crop_rect(skiatest::Reporter* reporter, GrContext* context) {
   1591     sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100));
   1592 
   1593     SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(100, 0, 20, 30),
   1594         SkImageFilter::CropRect::kHasWidth_CropEdge | SkImageFilter::CropRect::kHasHeight_CropEdge);
   1595     sk_sp<SkImageFilter> filter(make_grayscale(nullptr, &cropRect));
   1596     SkIPoint offset;
   1597     SkImageFilter::OutputProperties noColorSpace(nullptr);
   1598     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
   1599 
   1600     sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
   1601     REPORTER_ASSERT(reporter, resultImg);
   1602 
   1603     REPORTER_ASSERT(reporter, offset.fX == 0);
   1604     REPORTER_ASSERT(reporter, offset.fY == 0);
   1605     REPORTER_ASSERT(reporter, resultImg->width() == 20);
   1606     REPORTER_ASSERT(reporter, resultImg->height() == 30);
   1607 }
   1608 
   1609 DEF_TEST(ImageFilterPartialCropRect, reporter) {
   1610     test_partial_crop_rect(reporter, nullptr);
   1611 }
   1612 
   1613 #if SK_SUPPORT_GPU
   1614 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterPartialCropRect_Gpu, reporter, ctxInfo) {
   1615     test_partial_crop_rect(reporter, ctxInfo.grContext());
   1616 }
   1617 #endif
   1618 
   1619 DEF_TEST(ImageFilterCanComputeFastBounds, reporter) {
   1620 
   1621     {
   1622         SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
   1623         sk_sp<SkImageFilter> lighting(SkLightingImageFilter::MakePointLitDiffuse(location,
   1624                                                                                  SK_ColorGREEN,
   1625                                                                                  0, 0, nullptr));
   1626         REPORTER_ASSERT(reporter, !lighting->canComputeFastBounds());
   1627     }
   1628 
   1629     {
   1630         sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
   1631         REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
   1632         {
   1633             SkColorFilter* grayCF;
   1634             REPORTER_ASSERT(reporter, gray->asAColorFilter(&grayCF));
   1635             REPORTER_ASSERT(reporter, !grayCF->affectsTransparentBlack());
   1636             grayCF->unref();
   1637         }
   1638         REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
   1639 
   1640         sk_sp<SkImageFilter> grayBlur(SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1,
   1641                                                               std::move(gray)));
   1642         REPORTER_ASSERT(reporter, grayBlur->canComputeFastBounds());
   1643     }
   1644 
   1645     {
   1646         SkScalar greenMatrix[20] = { 0, 0, 0, 0, 0,
   1647                                      0, 0, 0, 0, 1,
   1648                                      0, 0, 0, 0, 0,
   1649                                      0, 0, 0, 0, 1 };
   1650         sk_sp<SkColorFilter> greenCF(SkColorFilter::MakeMatrixFilterRowMajor255(greenMatrix));
   1651         sk_sp<SkImageFilter> green(SkColorFilterImageFilter::Make(greenCF, nullptr));
   1652 
   1653         REPORTER_ASSERT(reporter, greenCF->affectsTransparentBlack());
   1654         REPORTER_ASSERT(reporter, !green->canComputeFastBounds());
   1655 
   1656         sk_sp<SkImageFilter> greenBlur(SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1,
   1657                                                                std::move(green)));
   1658         REPORTER_ASSERT(reporter, !greenBlur->canComputeFastBounds());
   1659     }
   1660 
   1661     uint8_t allOne[256], identity[256];
   1662     for (int i = 0; i < 256; ++i) {
   1663         identity[i] = i;
   1664         allOne[i] = 255;
   1665     }
   1666 
   1667     sk_sp<SkColorFilter> identityCF(SkTableColorFilter::MakeARGB(identity, identity,
   1668                                                                  identity, allOne));
   1669     sk_sp<SkImageFilter> identityFilter(SkColorFilterImageFilter::Make(identityCF, nullptr));
   1670     REPORTER_ASSERT(reporter, !identityCF->affectsTransparentBlack());
   1671     REPORTER_ASSERT(reporter, identityFilter->canComputeFastBounds());
   1672 
   1673     sk_sp<SkColorFilter> forceOpaqueCF(SkTableColorFilter::MakeARGB(allOne, identity,
   1674                                                                     identity, identity));
   1675     sk_sp<SkImageFilter> forceOpaque(SkColorFilterImageFilter::Make(forceOpaqueCF, nullptr));
   1676     REPORTER_ASSERT(reporter, forceOpaqueCF->affectsTransparentBlack());
   1677     REPORTER_ASSERT(reporter, !forceOpaque->canComputeFastBounds());
   1678 }
   1679 
   1680 // Verify that SkImageSource survives serialization
   1681 DEF_TEST(ImageFilterImageSourceSerialization, reporter) {
   1682     auto surface(SkSurface::MakeRasterN32Premul(10, 10));
   1683     surface->getCanvas()->clear(SK_ColorGREEN);
   1684     sk_sp<SkImage> image(surface->makeImageSnapshot());
   1685     sk_sp<SkImageFilter> filter(SkImageSource::Make(std::move(image)));
   1686 
   1687     sk_sp<SkData> data(SkValidatingSerializeFlattenable(filter.get()));
   1688     sk_sp<SkImageFilter> unflattenedFilter = SkValidatingDeserializeImageFilter(data->data(),
   1689                                                                                 data->size());
   1690     REPORTER_ASSERT(reporter, unflattenedFilter);
   1691 
   1692     SkBitmap bm;
   1693     bm.allocN32Pixels(10, 10);
   1694     bm.eraseColor(SK_ColorBLUE);
   1695     SkPaint paint;
   1696     paint.setColor(SK_ColorRED);
   1697     paint.setImageFilter(unflattenedFilter);
   1698 
   1699     SkCanvas canvas(bm);
   1700     canvas.drawRect(SkRect::MakeWH(10, 10), paint);
   1701     REPORTER_ASSERT(reporter, *bm.getAddr32(0, 0) == SkPreMultiplyColor(SK_ColorGREEN));
   1702 }
   1703 
   1704 static void test_large_blur_input(skiatest::Reporter* reporter, SkCanvas* canvas) {
   1705     SkBitmap largeBmp;
   1706     int largeW = 5000;
   1707     int largeH = 5000;
   1708 #if SK_SUPPORT_GPU
   1709     // If we're GPU-backed make the bitmap too large to be converted into a texture.
   1710     if (GrContext* ctx = canvas->getGrContext()) {
   1711         largeW = ctx->caps()->maxTextureSize() + 1;
   1712     }
   1713 #endif
   1714 
   1715     largeBmp.allocN32Pixels(largeW, largeH);
   1716     largeBmp.eraseColor(0);
   1717     if (!largeBmp.getPixels()) {
   1718         ERRORF(reporter, "Failed to allocate large bmp.");
   1719         return;
   1720     }
   1721 
   1722     sk_sp<SkImage> largeImage(SkImage::MakeFromBitmap(largeBmp));
   1723     if (!largeImage) {
   1724         ERRORF(reporter, "Failed to create large image.");
   1725         return;
   1726     }
   1727 
   1728     sk_sp<SkImageFilter> largeSource(SkImageSource::Make(std::move(largeImage)));
   1729     if (!largeSource) {
   1730         ERRORF(reporter, "Failed to create large SkImageSource.");
   1731         return;
   1732     }
   1733 
   1734     sk_sp<SkImageFilter> blur(SkBlurImageFilter::Make(10.f, 10.f, std::move(largeSource)));
   1735     if (!blur) {
   1736         ERRORF(reporter, "Failed to create SkBlurImageFilter.");
   1737         return;
   1738     }
   1739 
   1740     SkPaint paint;
   1741     paint.setImageFilter(std::move(blur));
   1742 
   1743     // This should not crash (http://crbug.com/570479).
   1744     canvas->drawRect(SkRect::MakeIWH(largeW, largeH), paint);
   1745 }
   1746 
   1747 DEF_TEST(ImageFilterBlurLargeImage, reporter) {
   1748     auto surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(100, 100)));
   1749     test_large_blur_input(reporter, surface->getCanvas());
   1750 }
   1751 
   1752 static void test_make_with_filter(skiatest::Reporter* reporter, GrContext* context) {
   1753     sk_sp<SkSurface> surface(create_surface(context, 192, 128));
   1754     surface->getCanvas()->clear(SK_ColorRED);
   1755     SkPaint bluePaint;
   1756     bluePaint.setColor(SK_ColorBLUE);
   1757     SkIRect subset = SkIRect::MakeXYWH(25, 20, 50, 50);
   1758     surface->getCanvas()->drawRect(SkRect::Make(subset), bluePaint);
   1759     sk_sp<SkImage> sourceImage = surface->makeImageSnapshot();
   1760 
   1761     sk_sp<SkImageFilter> filter = make_grayscale(nullptr, nullptr);
   1762     SkIRect clipBounds = SkIRect::MakeXYWH(30, 35, 100, 100);
   1763     SkIRect outSubset;
   1764     SkIPoint offset;
   1765     sk_sp<SkImage> result;
   1766 
   1767     result = sourceImage->makeWithFilter(nullptr, subset, clipBounds, &outSubset, &offset);
   1768     REPORTER_ASSERT(reporter, !result);
   1769 
   1770     result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, nullptr, &offset);
   1771     REPORTER_ASSERT(reporter, !result);
   1772 
   1773     result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, &outSubset, nullptr);
   1774     REPORTER_ASSERT(reporter, !result);
   1775 
   1776     SkIRect bigSubset = SkIRect::MakeXYWH(-10000, -10000, 20000, 20000);
   1777     result = sourceImage->makeWithFilter(filter.get(), bigSubset, clipBounds, &outSubset, &offset);
   1778     REPORTER_ASSERT(reporter, !result);
   1779 
   1780     SkIRect empty = SkIRect::MakeEmpty();
   1781     result = sourceImage->makeWithFilter(filter.get(), empty, clipBounds, &outSubset, &offset);
   1782     REPORTER_ASSERT(reporter, !result);
   1783 
   1784     result = sourceImage->makeWithFilter(filter.get(), subset, empty, &outSubset, &offset);
   1785     REPORTER_ASSERT(reporter, !result);
   1786 
   1787     SkIRect leftField = SkIRect::MakeXYWH(-1000, 0, 100, 100);
   1788     result = sourceImage->makeWithFilter(filter.get(), subset, leftField, &outSubset, &offset);
   1789     REPORTER_ASSERT(reporter, !result);
   1790 
   1791     result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, &outSubset, &offset);
   1792 
   1793     REPORTER_ASSERT(reporter, result);
   1794     REPORTER_ASSERT(reporter, result->bounds().contains(outSubset));
   1795     SkIRect destRect = SkIRect::MakeXYWH(offset.x(), offset.y(),
   1796                                           outSubset.width(), outSubset.height());
   1797     REPORTER_ASSERT(reporter, clipBounds.contains(destRect));
   1798 
   1799     // In GPU-mode, this case creates a special image with a backing size that differs from
   1800     // the content size
   1801     {
   1802         clipBounds.setXYWH(0, 0, 170, 100);
   1803         subset.setXYWH(0, 0, 160, 90);
   1804 
   1805         filter = SkXfermodeImageFilter::Make(SkBlendMode::kSrc, nullptr);
   1806         result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, &outSubset, &offset);
   1807         REPORTER_ASSERT(reporter, result);
   1808     }
   1809 }
   1810 
   1811 DEF_TEST(ImageFilterMakeWithFilter, reporter) {
   1812     test_make_with_filter(reporter, nullptr);
   1813 }
   1814 
   1815 #if SK_SUPPORT_GPU
   1816 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMakeWithFilter_Gpu, reporter, ctxInfo) {
   1817     test_make_with_filter(reporter, ctxInfo.grContext());
   1818 }
   1819 #endif
   1820 
   1821 #if SK_SUPPORT_GPU
   1822 
   1823 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterHugeBlur_Gpu, reporter, ctxInfo) {
   1824 
   1825     sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(ctxInfo.grContext(),
   1826                                                       SkBudgeted::kNo,
   1827                                                       SkImageInfo::MakeN32Premul(100, 100)));
   1828 
   1829 
   1830     SkCanvas* canvas = surf->getCanvas();
   1831 
   1832     test_huge_blur(canvas, reporter);
   1833 }
   1834 
   1835 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(XfermodeImageFilterCroppedInput_Gpu, reporter, ctxInfo) {
   1836 
   1837     sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(ctxInfo.grContext(),
   1838                                                       SkBudgeted::kNo,
   1839                                                       SkImageInfo::MakeN32Premul(1, 1)));
   1840 
   1841 
   1842     SkCanvas* canvas = surf->getCanvas();
   1843 
   1844     test_xfermode_cropped_input(canvas, reporter);
   1845 }
   1846 
   1847 DEF_GPUTEST_FOR_ALL_CONTEXTS(ImageFilterBlurLargeImage_Gpu, reporter, ctxInfo) {
   1848     auto surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kYes,
   1849                                              SkImageInfo::MakeN32Premul(100, 100)));
   1850     test_large_blur_input(reporter, surface->getCanvas());
   1851 }
   1852 #endif
   1853 
   1854 /*
   1855  *  Test that colorfilterimagefilter does not require its CTM to be decomposed when it has more
   1856  *  than just scale/translate, but that other filters do.
   1857  */
   1858 DEF_TEST(ImageFilterComplexCTM, reporter) {
   1859     // just need a colorfilter to exercise the corresponding imagefilter
   1860     sk_sp<SkColorFilter> cf = SkColorFilter::MakeModeFilter(SK_ColorRED, SkBlendMode::kSrcATop);
   1861     sk_sp<SkImageFilter> cfif = SkColorFilterImageFilter::Make(cf, nullptr);    // can handle
   1862     sk_sp<SkImageFilter> blif = SkBlurImageFilter::Make(3, 3, nullptr);         // cannot handle
   1863 
   1864     struct {
   1865         sk_sp<SkImageFilter> fFilter;
   1866         bool                 fExpectCanHandle;
   1867     } recs[] = {
   1868         { cfif,                                     true  },
   1869         { SkColorFilterImageFilter::Make(cf, cfif), true  },
   1870         { SkMergeImageFilter::Make(cfif, cfif, SkBlendMode::kSrcOver),     true  },
   1871         { SkComposeImageFilter::Make(cfif, cfif),   true  },
   1872 
   1873         { blif,                                     false },
   1874         { SkBlurImageFilter::Make(3, 3, cfif),      false },
   1875         { SkColorFilterImageFilter::Make(cf, blif), false },
   1876         { SkMergeImageFilter::Make(cfif, blif, SkBlendMode::kSrcOver),     false },
   1877         { SkComposeImageFilter::Make(blif, cfif),   false },
   1878     };
   1879 
   1880     for (const auto& rec : recs) {
   1881         const bool canHandle = rec.fFilter->canHandleComplexCTM();
   1882         REPORTER_ASSERT(reporter, canHandle == rec.fExpectCanHandle);
   1883     }
   1884 }
   1885