Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2014 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 "Test.h"
      9 #include "RecordTestUtils.h"
     10 
     11 #include "SkBlurImageFilter.h"
     12 #include "SkColorFilter.h"
     13 #include "SkRecord.h"
     14 #include "SkRecordOpts.h"
     15 #include "SkRecorder.h"
     16 #include "SkRecords.h"
     17 #include "SkPictureRecorder.h"
     18 #include "SkPictureImageFilter.h"
     19 #include "SkSurface.h"
     20 
     21 static const int W = 1920, H = 1080;
     22 
     23 DEF_TEST(RecordOpts_NoopDraw, r) {
     24     SkRecord record;
     25     SkRecorder recorder(&record, W, H);
     26 
     27     recorder.drawRect(SkRect::MakeWH(200, 200), SkPaint());
     28     recorder.drawRect(SkRect::MakeWH(300, 300), SkPaint());
     29     recorder.drawRect(SkRect::MakeWH(100, 100), SkPaint());
     30 
     31     record.replace<SkRecords::NoOp>(1);  // NoOps should be allowed.
     32 
     33     SkRecordNoopSaveRestores(&record);
     34 
     35     REPORTER_ASSERT(r, 2 == count_instances_of_type<SkRecords::DrawRect>(record));
     36 }
     37 
     38 DEF_TEST(RecordOpts_SingleNoopSaveRestore, r) {
     39     SkRecord record;
     40     SkRecorder recorder(&record, W, H);
     41 
     42     recorder.save();
     43         recorder.clipRect(SkRect::MakeWH(200, 200));
     44     recorder.restore();
     45 
     46     SkRecordNoopSaveRestores(&record);
     47     for (int i = 0; i < 3; i++) {
     48         assert_type<SkRecords::NoOp>(r, record, i);
     49     }
     50 }
     51 
     52 DEF_TEST(RecordOpts_NoopSaveRestores, r) {
     53     SkRecord record;
     54     SkRecorder recorder(&record, W, H);
     55 
     56     // The second pass will clean up this pair after the first pass noops all the innards.
     57     recorder.save();
     58         // A simple pointless pair of save/restore.
     59         recorder.save();
     60         recorder.restore();
     61 
     62         // As long as we don't draw in there, everything is a noop.
     63         recorder.save();
     64             recorder.clipRect(SkRect::MakeWH(200, 200));
     65             recorder.clipRect(SkRect::MakeWH(100, 100));
     66         recorder.restore();
     67     recorder.restore();
     68 
     69     SkRecordNoopSaveRestores(&record);
     70     for (int index = 0; index < record.count(); index++) {
     71         assert_type<SkRecords::NoOp>(r, record, index);
     72     }
     73 }
     74 
     75 DEF_TEST(RecordOpts_SaveSaveLayerRestoreRestore, r) {
     76     SkRecord record;
     77     SkRecorder recorder(&record, W, H);
     78 
     79     // A previous bug NoOp'd away the first 3 commands.
     80     recorder.save();
     81         recorder.saveLayer(nullptr, nullptr);
     82         recorder.restore();
     83     recorder.restore();
     84 
     85     SkRecordNoopSaveRestores(&record);
     86     switch (record.count()) {
     87         case 4:
     88             assert_type<SkRecords::Save>     (r, record, 0);
     89             assert_type<SkRecords::SaveLayer>(r, record, 1);
     90             assert_type<SkRecords::Restore>  (r, record, 2);
     91             assert_type<SkRecords::Restore>  (r, record, 3);
     92             break;
     93         case 2:
     94             assert_type<SkRecords::SaveLayer>(r, record, 0);
     95             assert_type<SkRecords::Restore>  (r, record, 1);
     96             break;
     97         case 0:
     98             break;
     99         default:
    100             REPORTER_ASSERT(r, false);
    101     }
    102 }
    103 
    104 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
    105 static void assert_savelayer_restore(skiatest::Reporter* r,
    106                                      SkRecord* record,
    107                                      int i,
    108                                      bool shouldBeNoOped) {
    109     SkRecordNoopSaveLayerDrawRestores(record);
    110     if (shouldBeNoOped) {
    111         assert_type<SkRecords::NoOp>(r, *record, i);
    112         assert_type<SkRecords::NoOp>(r, *record, i+1);
    113     } else {
    114         assert_type<SkRecords::SaveLayer>(r, *record, i);
    115         assert_type<SkRecords::Restore>(r, *record, i+1);
    116     }
    117 }
    118 
    119 static void assert_savelayer_draw_restore(skiatest::Reporter* r,
    120                                           SkRecord* record,
    121                                           int i,
    122                                           bool shouldBeNoOped) {
    123     SkRecordNoopSaveLayerDrawRestores(record);
    124     if (shouldBeNoOped) {
    125         assert_type<SkRecords::NoOp>(r, *record, i);
    126         assert_type<SkRecords::NoOp>(r, *record, i+2);
    127     } else {
    128         assert_type<SkRecords::SaveLayer>(r, *record, i);
    129         assert_type<SkRecords::Restore>(r, *record, i+2);
    130     }
    131 }
    132 
    133 DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore, r) {
    134     SkRecord record;
    135     SkRecorder recorder(&record, W, H);
    136 
    137     SkRect bounds = SkRect::MakeWH(100, 200);
    138     SkRect   draw = SkRect::MakeWH(50, 60);
    139 
    140     SkPaint alphaOnlyLayerPaint, translucentLayerPaint, xfermodeLayerPaint;
    141     alphaOnlyLayerPaint.setColor(0x03000000);  // Only alpha.
    142     translucentLayerPaint.setColor(0x03040506);  // Not only alpha.
    143     xfermodeLayerPaint.setBlendMode(SkBlendMode::kDstIn);  // Any effect will do.
    144 
    145     SkPaint opaqueDrawPaint, translucentDrawPaint;
    146     opaqueDrawPaint.setColor(0xFF020202);  // Opaque.
    147     translucentDrawPaint.setColor(0x0F020202);  // Not opaque.
    148 
    149     // SaveLayer/Restore removed: No paint = no point.
    150     recorder.saveLayer(nullptr, nullptr);
    151         recorder.drawRect(draw, opaqueDrawPaint);
    152     recorder.restore();
    153     assert_savelayer_draw_restore(r, &record, 0, true);
    154 
    155     // Bounds don't matter.
    156     recorder.saveLayer(&bounds, nullptr);
    157         recorder.drawRect(draw, opaqueDrawPaint);
    158     recorder.restore();
    159     assert_savelayer_draw_restore(r, &record, 3, true);
    160 
    161     // TODO(mtklein): test case with null draw paint
    162 
    163     // No change: layer paint isn't alpha-only.
    164     recorder.saveLayer(nullptr, &translucentLayerPaint);
    165         recorder.drawRect(draw, opaqueDrawPaint);
    166     recorder.restore();
    167     assert_savelayer_draw_restore(r, &record, 6, false);
    168 
    169     // No change: layer paint has an effect.
    170     recorder.saveLayer(nullptr, &xfermodeLayerPaint);
    171         recorder.drawRect(draw, opaqueDrawPaint);
    172     recorder.restore();
    173     assert_savelayer_draw_restore(r, &record, 9, false);
    174 
    175     // SaveLayer/Restore removed: we can fold in the alpha!
    176     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
    177         recorder.drawRect(draw, translucentDrawPaint);
    178     recorder.restore();
    179     assert_savelayer_draw_restore(r, &record, 12, true);
    180 
    181     // SaveLayer/Restore removed: we can fold in the alpha!
    182     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
    183         recorder.drawRect(draw, opaqueDrawPaint);
    184     recorder.restore();
    185     assert_savelayer_draw_restore(r, &record, 15, true);
    186 
    187     const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, record, 16);
    188     REPORTER_ASSERT(r, drawRect != nullptr);
    189     REPORTER_ASSERT(r, drawRect->paint.getColor() == 0x03020202);
    190 
    191     // saveLayer w/ backdrop should NOT go away
    192     sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(3, 3, nullptr));
    193     recorder.saveLayer({ nullptr, nullptr, filter.get(), nullptr, nullptr, 0});
    194         recorder.drawRect(draw, opaqueDrawPaint);
    195     recorder.restore();
    196     assert_savelayer_draw_restore(r, &record, 18, false);
    197 
    198     // saveLayer w/ clip mask should also NOT go away
    199     {
    200         sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(10, 10));
    201         recorder.saveLayer({ nullptr, nullptr, nullptr, surface->makeImageSnapshot().get(),
    202                              nullptr, 0});
    203             recorder.drawRect(draw, opaqueDrawPaint);
    204         recorder.restore();
    205         assert_savelayer_draw_restore(r, &record, 21, false);
    206     }
    207 }
    208 #endif
    209 
    210 static void assert_merge_svg_opacity_and_filter_layers(skiatest::Reporter* r,
    211                                                        SkRecord* record,
    212                                                        int i,
    213                                                        bool shouldBeNoOped) {
    214     SkRecordMergeSvgOpacityAndFilterLayers(record);
    215     if (shouldBeNoOped) {
    216         assert_type<SkRecords::NoOp>(r, *record, i);
    217         assert_type<SkRecords::NoOp>(r, *record, i + 6);
    218     } else {
    219         assert_type<SkRecords::SaveLayer>(r, *record, i);
    220         assert_type<SkRecords::Restore>(r, *record, i + 6);
    221     }
    222 }
    223 
    224 DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) {
    225     SkRecord record;
    226     SkRecorder recorder(&record, W, H);
    227 
    228     SkRect bounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(200));
    229     SkRect clip = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(60));
    230 
    231     SkPaint alphaOnlyLayerPaint;
    232     alphaOnlyLayerPaint.setColor(0x03000000);  // Only alpha.
    233     SkPaint translucentLayerPaint;
    234     translucentLayerPaint.setColor(0x03040506);  // Not only alpha.
    235     SkPaint xfermodePaint;
    236     xfermodePaint.setBlendMode(SkBlendMode::kDstIn);
    237     SkPaint colorFilterPaint;
    238     colorFilterPaint.setColorFilter(
    239         SkColorFilter::MakeModeFilter(SK_ColorLTGRAY, SkBlendMode::kSrcIn));
    240 
    241     SkPaint opaqueFilterLayerPaint;
    242     opaqueFilterLayerPaint.setColor(0xFF020202);  // Opaque.
    243     SkPaint translucentFilterLayerPaint;
    244     translucentFilterLayerPaint.setColor(0x0F020202);  // Not opaque.
    245     sk_sp<SkPicture> shape;
    246     {
    247         SkPictureRecorder recorder;
    248         SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(100), SkIntToScalar(100));
    249         SkPaint shapePaint;
    250         shapePaint.setColor(SK_ColorWHITE);
    251         canvas->drawRect(SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50)), shapePaint);
    252         shape = recorder.finishRecordingAsPicture();
    253     }
    254     translucentFilterLayerPaint.setImageFilter(SkPictureImageFilter::Make(shape));
    255 
    256     int index = 0;
    257 
    258     {
    259         sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(3, 3, nullptr));
    260         // first (null) should be optimized, 2nd should not
    261         SkImageFilter* filters[] = { nullptr, filter.get() };
    262 
    263         // Any combination of these should cause the pattern to be optimized.
    264         SkRect* firstBounds[] = { nullptr, &bounds };
    265         SkPaint* firstPaints[] = { nullptr, &alphaOnlyLayerPaint };
    266         SkRect* secondBounds[] = { nullptr, &bounds };
    267         SkPaint* secondPaints[] = { &opaqueFilterLayerPaint, &translucentFilterLayerPaint };
    268 
    269         for (auto outerF : filters) {
    270             bool outerNoOped = !outerF;
    271             for (auto innerF : filters) {
    272                 for (size_t i = 0; i < SK_ARRAY_COUNT(firstBounds); ++ i) {
    273                     for (size_t j = 0; j < SK_ARRAY_COUNT(firstPaints); ++j) {
    274                         for (size_t k = 0; k < SK_ARRAY_COUNT(secondBounds); ++k) {
    275                             for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) {
    276                                 bool innerNoOped = !secondBounds[k] && !secondPaints[m] && !innerF;
    277 
    278                                 recorder.saveLayer({firstBounds[i], firstPaints[j], outerF,
    279                                                     nullptr, nullptr, 0});
    280                                 recorder.save();
    281                                 recorder.clipRect(clip);
    282                                 recorder.saveLayer({secondBounds[k], secondPaints[m], innerF,
    283                                                     nullptr, nullptr, 0});
    284                                 recorder.restore();
    285                                 recorder.restore();
    286                                 recorder.restore();
    287                                 assert_merge_svg_opacity_and_filter_layers(r, &record, index,
    288                                                                            outerNoOped);
    289                             #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
    290                                 assert_savelayer_restore(r, &record, index + 3, innerNoOped);
    291                             #endif
    292                                 index += 7;
    293                             }
    294                         }
    295                     }
    296                 }
    297             }
    298         }
    299     }
    300 
    301     // These should cause the pattern to stay unoptimized:
    302     struct {
    303         SkPaint* firstPaint;
    304         SkPaint* secondPaint;
    305     } noChangeTests[] = {
    306         // No change: nullptr filter layer paint not implemented.
    307         { &alphaOnlyLayerPaint, nullptr },
    308         // No change: layer paint is not alpha-only.
    309         { &translucentLayerPaint, &opaqueFilterLayerPaint },
    310         // No change: layer paint has an xfereffect.
    311         { &xfermodePaint, &opaqueFilterLayerPaint },
    312         // No change: filter layer paint has an xfereffect.
    313         { &alphaOnlyLayerPaint, &xfermodePaint },
    314         // No change: layer paint has a color filter.
    315         { &colorFilterPaint, &opaqueFilterLayerPaint },
    316         // No change: filter layer paint has a color filter (until the optimization accounts for
    317         // constant color draws that can filter the color).
    318         { &alphaOnlyLayerPaint, &colorFilterPaint }
    319     };
    320 
    321     for (size_t i = 0; i < SK_ARRAY_COUNT(noChangeTests); ++i) {
    322         recorder.saveLayer(nullptr, noChangeTests[i].firstPaint);
    323         recorder.save();
    324         recorder.clipRect(clip);
    325         recorder.saveLayer(nullptr, noChangeTests[i].secondPaint);
    326         recorder.restore();
    327         recorder.restore();
    328         recorder.restore();
    329         assert_merge_svg_opacity_and_filter_layers(r, &record, index, false);
    330         index += 7;
    331     }
    332 
    333     // Test the folded alpha value.
    334     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
    335     recorder.save();
    336     recorder.clipRect(clip);
    337     recorder.saveLayer(nullptr, &opaqueFilterLayerPaint);
    338     recorder.restore();
    339     recorder.restore();
    340     recorder.restore();
    341     assert_merge_svg_opacity_and_filter_layers(r, &record, index, true);
    342 
    343     const SkRecords::SaveLayer* saveLayer = assert_type<SkRecords::SaveLayer>(r, record, index + 3);
    344     REPORTER_ASSERT(r, saveLayer != nullptr);
    345     REPORTER_ASSERT(r, saveLayer->paint->getColor() == 0x03020202);
    346 
    347     index += 7;
    348 
    349     // Test that currently we do not fold alphas for patterns without the clip. This is just not
    350     // implemented.
    351     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
    352     recorder.saveLayer(nullptr, &opaqueFilterLayerPaint);
    353     recorder.restore();
    354     recorder.restore();
    355     SkRecordMergeSvgOpacityAndFilterLayers(&record);
    356     assert_type<SkRecords::SaveLayer>(r, record, index);
    357     assert_type<SkRecords::SaveLayer>(r, record, index + 1);
    358     assert_type<SkRecords::Restore>(r, record, index + 2);
    359     assert_type<SkRecords::Restore>(r, record, index + 3);
    360     index += 4;
    361 }
    362 
    363 static void do_draw(SkCanvas* canvas, SkColor color, bool doLayer) {
    364     canvas->drawColor(SK_ColorWHITE);
    365 
    366     SkPaint p;
    367     p.setColor(color);
    368 
    369     if (doLayer) {
    370         canvas->saveLayer(nullptr, nullptr);
    371         p.setBlendMode(SkBlendMode::kSrc);
    372         canvas->drawPaint(p);
    373         canvas->restore();
    374     } else {
    375         canvas->drawPaint(p);
    376     }
    377 }
    378 
    379 static bool is_equal(SkSurface* a, SkSurface* b) {
    380     const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
    381     SkPMColor ca, cb;
    382     a->readPixels(info, &ca, sizeof(SkPMColor), 0, 0);
    383     b->readPixels(info, &cb, sizeof(SkPMColor), 0, 0);
    384     return ca == cb;
    385 }
    386 
    387 // Test drawing w/ and w/o a simple layer (no bounds or paint), so see that drawing ops
    388 // that *should* draw the same in fact do.
    389 //
    390 // Perform this test twice : once directly, and once via a picture
    391 //
    392 static void do_savelayer_srcmode(skiatest::Reporter* r, SkColor color) {
    393     for (int doPicture = 0; doPicture <= 1; ++doPicture) {
    394         sk_sp<SkSurface> surf0 = SkSurface::MakeRasterN32Premul(10, 10);
    395         sk_sp<SkSurface> surf1 = SkSurface::MakeRasterN32Premul(10, 10);
    396         SkCanvas* c0 = surf0->getCanvas();
    397         SkCanvas* c1 = surf1->getCanvas();
    398 
    399         SkPictureRecorder rec0, rec1;
    400         if (doPicture) {
    401             c0 = rec0.beginRecording(10, 10);
    402             c1 = rec1.beginRecording(10, 10);
    403         }
    404 
    405         do_draw(c0, color, false);
    406         do_draw(c1, color, true);
    407 
    408         if (doPicture) {
    409             surf0->getCanvas()->drawPicture(rec0.finishRecordingAsPicture());
    410             surf1->getCanvas()->drawPicture(rec1.finishRecordingAsPicture());
    411         }
    412 
    413         // we replicate the assert so we can see which line is reported if there is a failure
    414         if (doPicture) {
    415             REPORTER_ASSERT(r, is_equal(surf0.get(), surf1.get()));
    416         } else {
    417             REPORTER_ASSERT(r, is_equal(surf0.get(), surf1.get()));
    418         }
    419     }
    420 }
    421 
    422 DEF_TEST(savelayer_srcmode_opaque, r) {
    423     do_savelayer_srcmode(r, SK_ColorRED);
    424 }
    425 
    426 DEF_TEST(savelayer_srcmode_alpha, r) {
    427     do_savelayer_srcmode(r, 0x80FF0000);
    428 }
    429 
    430