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 "CanvasStateHelpers.h"
      9 #include "SkCanvas.h"
     10 #include "SkClipOpPriv.h"
     11 #include "SkCanvasStateUtils.h"
     12 #include "SkCommandLineFlags.h"
     13 #include "SkDrawFilter.h"
     14 #include "SkPaint.h"
     15 #include "SkRegion.h"
     16 #include "SkRRect.h"
     17 #include "SkRect.h"
     18 #include "SkTLazy.h"
     19 #include "Test.h"
     20 
     21 // dlopen and the library flag are only used for tests which require this flag.
     22 #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
     23 #include <dlfcn.h>
     24 
     25 DEFINE_string(library, "", "Support library to use for CanvasState test. If empty (the default), "
     26                            "the test will be run without crossing a library boundary. Otherwise, "
     27                            "it is expected to be a full path to a shared library file, which will"
     28                            " be dynamically loaded. Functions from the library will be called to "
     29                            "test SkCanvasState. Instructions for generating the library are in "
     30                            "gyp/canvas_state_lib.gyp");
     31 
     32 
     33 // This class calls dlopen on the library passed in to the command line flag library, and handles
     34 // calling dlclose when it goes out of scope.
     35 class OpenLibResult {
     36 public:
     37     // If the flag library was passed to this run of the test, attempt to open it using dlopen and
     38     // report whether it succeeded.
     39     OpenLibResult(skiatest::Reporter* reporter) {
     40         if (FLAGS_library.count() == 1) {
     41             fHandle = dlopen(FLAGS_library[0], RTLD_LAZY | RTLD_LOCAL);
     42             REPORTER_ASSERT_MESSAGE(reporter, fHandle != nullptr, "Failed to open library!");
     43         } else {
     44             fHandle = nullptr;
     45         }
     46     }
     47 
     48     // Automatically call dlclose when going out of scope.
     49     ~OpenLibResult() {
     50         if (fHandle) {
     51             dlclose(fHandle);
     52         }
     53     }
     54 
     55     // Pointer to the shared library object.
     56     void* handle() { return fHandle; }
     57 
     58 private:
     59     void* fHandle;
     60 };
     61 
     62 DEF_TEST(CanvasState_test_complex_layers, reporter) {
     63     const int WIDTH = 400;
     64     const int HEIGHT = 400;
     65     const int SPACER = 10;
     66 
     67     SkRect rect = SkRect::MakeXYWH(SkIntToScalar(SPACER), SkIntToScalar(SPACER),
     68                                    SkIntToScalar(WIDTH-(2*SPACER)),
     69                                    SkIntToScalar((HEIGHT-(2*SPACER)) / 7));
     70 
     71     const SkColorType colorTypes[] = {
     72         kRGB_565_SkColorType, kN32_SkColorType
     73     };
     74 
     75     const int layerAlpha[] = { 255, 255, 0 };
     76     const SkCanvas::SaveLayerFlags flags[] = {
     77         static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag),
     78         0,
     79         static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag),
     80     };
     81     REPORTER_ASSERT(reporter, sizeof(layerAlpha) == sizeof(flags));
     82 
     83     bool (*drawFn)(SkCanvasState* state, float l, float t,
     84                    float r, float b, int32_t s);
     85 
     86     OpenLibResult openLibResult(reporter);
     87     if (openLibResult.handle() != nullptr) {
     88         *(void**) (&drawFn) = dlsym(openLibResult.handle(),
     89                                     "complex_layers_draw_from_canvas_state");
     90     } else {
     91         drawFn = complex_layers_draw_from_canvas_state;
     92     }
     93 
     94     REPORTER_ASSERT(reporter, drawFn);
     95     if (!drawFn) {
     96         return;
     97     }
     98 
     99     for (size_t i = 0; i < SK_ARRAY_COUNT(colorTypes); ++i) {
    100         SkBitmap bitmaps[2];
    101         for (int j = 0; j < 2; ++j) {
    102             bitmaps[j].allocPixels(SkImageInfo::Make(WIDTH, HEIGHT,
    103                                                      colorTypes[i],
    104                                                      kPremul_SkAlphaType));
    105 
    106             SkCanvas canvas(bitmaps[j]);
    107 
    108             canvas.drawColor(SK_ColorRED);
    109 
    110             for (size_t k = 0; k < SK_ARRAY_COUNT(layerAlpha); ++k) {
    111                 SkTLazy<SkPaint> paint;
    112                 if (layerAlpha[k] != 0xFF) {
    113                     paint.init()->setAlpha(layerAlpha[k]);
    114                 }
    115 
    116                 // draw a rect within the layer's bounds and again outside the layer's bounds
    117                 canvas.saveLayer(SkCanvas::SaveLayerRec(&rect, paint.getMaybeNull(), flags[k]));
    118 
    119                 if (j) {
    120                     // Capture from the first Skia.
    121                     SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
    122                     REPORTER_ASSERT(reporter, state);
    123 
    124                     // And draw to it in the second Skia.
    125                     bool success = complex_layers_draw_from_canvas_state(state,
    126                             rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, SPACER);
    127                     REPORTER_ASSERT(reporter, success);
    128 
    129                     // And release it in the *first* Skia.
    130                     SkCanvasStateUtils::ReleaseCanvasState(state);
    131                 } else {
    132                     // Draw in the first Skia.
    133                     complex_layers_draw(&canvas, rect.fLeft, rect.fTop,
    134                                         rect.fRight, rect.fBottom, SPACER);
    135                 }
    136 
    137                 canvas.restore();
    138 
    139                 // translate the canvas for the next iteration
    140                 canvas.translate(0, 2*(rect.height() + SPACER));
    141             }
    142         }
    143 
    144         // now we memcmp the two bitmaps
    145         REPORTER_ASSERT(reporter, bitmaps[0].getSize() == bitmaps[1].getSize());
    146         REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(),
    147                                           bitmaps[1].getPixels(),
    148                                           bitmaps[0].getSize()));
    149     }
    150 }
    151 #endif
    152 
    153 ////////////////////////////////////////////////////////////////////////////////
    154 
    155 #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
    156 DEF_TEST(CanvasState_test_complex_clips, reporter) {
    157     const int WIDTH = 400;
    158     const int HEIGHT = 400;
    159     const int SPACER = 10;
    160 
    161     SkIRect layerRect = SkIRect::MakeWH(WIDTH, HEIGHT / 4);
    162     layerRect.inset(2*SPACER, 2*SPACER);
    163 
    164     SkIRect clipRect = layerRect;
    165     clipRect.fRight = clipRect.fLeft + (clipRect.width() / 2) - (2*SPACER);
    166     clipRect.outset(SPACER, SPACER);
    167 
    168     SkIRect regionBounds = clipRect;
    169     regionBounds.offset(clipRect.width() + (2*SPACER), 0);
    170 
    171     SkIRect regionInterior = regionBounds;
    172     regionInterior.inset(SPACER*3, SPACER*3);
    173 
    174     SkRegion clipRegion;
    175     clipRegion.setRect(regionBounds);
    176     clipRegion.op(regionInterior, SkRegion::kDifference_Op);
    177 
    178 
    179     const SkRegion::Op clipOps[] = { SkRegion::kIntersect_Op,
    180                                      SkRegion::kIntersect_Op,
    181                                      SkRegion::kReplace_Op,
    182     };
    183     const SkCanvas::SaveLayerFlags flags[] = {
    184         static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag),
    185         0,
    186         static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag),
    187     };
    188     REPORTER_ASSERT(reporter, sizeof(clipOps) == sizeof(flags));
    189 
    190     bool (*drawFn)(SkCanvasState* state, int32_t l, int32_t t,
    191                    int32_t r, int32_t b, int32_t clipOp,
    192                    int32_t regionRects, int32_t* rectCoords);
    193 
    194     OpenLibResult openLibResult(reporter);
    195     if (openLibResult.handle() != nullptr) {
    196         *(void**) (&drawFn) = dlsym(openLibResult.handle(),
    197                                     "complex_clips_draw_from_canvas_state");
    198     } else {
    199         drawFn = complex_clips_draw_from_canvas_state;
    200     }
    201 
    202     REPORTER_ASSERT(reporter, drawFn);
    203     if (!drawFn) {
    204         return;
    205     }
    206 
    207     SkBitmap bitmaps[2];
    208     for (int i = 0; i < 2; ++i) {
    209         bitmaps[i].allocN32Pixels(WIDTH, HEIGHT);
    210 
    211         SkCanvas canvas(bitmaps[i]);
    212 
    213         canvas.drawColor(SK_ColorRED);
    214 
    215         SkRegion localRegion = clipRegion;
    216 
    217         SkPaint paint;
    218         paint.setAlpha(128);
    219         for (size_t j = 0; j < SK_ARRAY_COUNT(flags); ++j) {
    220             SkRect layerBounds = SkRect::Make(layerRect);
    221             canvas.saveLayer(SkCanvas::SaveLayerRec(&layerBounds, &paint, flags[j]));
    222 
    223             if (i) {
    224                 SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
    225                 REPORTER_ASSERT(reporter, state);
    226 
    227                 SkRegion::Iterator iter(localRegion);
    228                 SkTDArray<int32_t> rectCoords;
    229                 for (; !iter.done(); iter.next()) {
    230                     const SkIRect& rect = iter.rect();
    231                     *rectCoords.append() = rect.fLeft;
    232                     *rectCoords.append() = rect.fTop;
    233                     *rectCoords.append() = rect.fRight;
    234                     *rectCoords.append() = rect.fBottom;
    235                 }
    236                 bool success = drawFn(state, clipRect.fLeft, clipRect.fTop,
    237                                       clipRect.fRight, clipRect.fBottom, clipOps[j],
    238                                       rectCoords.count() / 4, rectCoords.begin());
    239                 REPORTER_ASSERT(reporter, success);
    240 
    241                 SkCanvasStateUtils::ReleaseCanvasState(state);
    242             } else {
    243                 complex_clips_draw(&canvas, clipRect.fLeft, clipRect.fTop,
    244                                    clipRect.fRight, clipRect.fBottom, clipOps[j],
    245                                    localRegion);
    246             }
    247 
    248             canvas.restore();
    249 
    250             // translate the canvas and region for the next iteration
    251             canvas.translate(0, SkIntToScalar(2*(layerRect.height() + (SPACER))));
    252             localRegion.translate(0, 2*(layerRect.height() + SPACER));
    253         }
    254     }
    255 
    256     // now we memcmp the two bitmaps
    257     REPORTER_ASSERT(reporter, bitmaps[0].getSize() == bitmaps[1].getSize());
    258     REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(),
    259                                       bitmaps[1].getPixels(),
    260                                       bitmaps[0].getSize()));
    261 }
    262 #endif
    263 
    264 ////////////////////////////////////////////////////////////////////////////////
    265 
    266 #ifdef SK_SUPPORT_LEGACY_DRAWFILTER
    267 
    268 class TestDrawFilter : public SkDrawFilter {
    269 public:
    270     bool filter(SkPaint*, Type) override { return true; }
    271 };
    272 
    273 DEF_TEST(CanvasState_test_draw_filters, reporter) {
    274     TestDrawFilter drawFilter;
    275     SkBitmap bitmap;
    276     bitmap.allocN32Pixels(10, 10);
    277     SkCanvas canvas(bitmap);
    278 
    279     canvas.setDrawFilter(&drawFilter);
    280 
    281     SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
    282     REPORTER_ASSERT(reporter, state);
    283     std::unique_ptr<SkCanvas> tmpCanvas = SkCanvasStateUtils::MakeFromCanvasState(state);
    284     REPORTER_ASSERT(reporter, tmpCanvas);
    285 
    286     REPORTER_ASSERT(reporter, canvas.getDrawFilter());
    287     REPORTER_ASSERT(reporter, nullptr == tmpCanvas->getDrawFilter());
    288 
    289     SkCanvasStateUtils::ReleaseCanvasState(state);
    290 }
    291 
    292 #endif
    293 
    294 ////////////////////////////////////////////////////////////////////////////////
    295 
    296 DEF_TEST(CanvasState_test_soft_clips, reporter) {
    297     SkBitmap bitmap;
    298     bitmap.allocN32Pixels(10, 10);
    299     SkCanvas canvas(bitmap);
    300 
    301     SkRRect roundRect;
    302     roundRect.setOval(SkRect::MakeWH(5, 5));
    303 
    304     canvas.clipRRect(roundRect, kIntersect_SkClipOp, true);
    305 
    306     SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
    307     REPORTER_ASSERT(reporter, !state);
    308 }
    309 
    310 DEF_TEST(CanvasState_test_saveLayer_clip, reporter) {
    311     const uint32_t dontSaveFlag = 1 << 31;    // secret flag for don't save
    312 #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
    313     static_assert(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag == dontSaveFlag, "");
    314 #endif
    315     const int WIDTH = 100;
    316     const int HEIGHT = 100;
    317     const int LAYER_WIDTH = 50;
    318     const int LAYER_HEIGHT = 50;
    319 
    320     SkBitmap bitmap;
    321     bitmap.allocN32Pixels(WIDTH, HEIGHT);
    322     SkCanvas canvas(bitmap);
    323 
    324     SkRect bounds = SkRect::MakeWH(SkIntToScalar(LAYER_WIDTH), SkIntToScalar(LAYER_HEIGHT));
    325     canvas.clipRect(SkRect::MakeWH(SkIntToScalar(WIDTH), SkIntToScalar(HEIGHT)));
    326 
    327     SkIRect devClip;
    328     // Check that saveLayer without the kClipToLayer_SaveFlag leaves the clip unchanged.
    329     canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds, nullptr, dontSaveFlag));
    330     devClip = canvas.getDeviceClipBounds();
    331     REPORTER_ASSERT(reporter, canvas.isClipRect());
    332     REPORTER_ASSERT(reporter, devClip.width() == WIDTH);
    333     REPORTER_ASSERT(reporter, devClip.height() == HEIGHT);
    334     canvas.restore();
    335 
    336     // Check that saveLayer with the kClipToLayer_SaveFlag sets the clip
    337     // stack to the layer bounds.
    338     canvas.saveLayer(&bounds, nullptr);
    339     devClip = canvas.getDeviceClipBounds();
    340     REPORTER_ASSERT(reporter, canvas.isClipRect());
    341     REPORTER_ASSERT(reporter, devClip.width() == LAYER_WIDTH);
    342     REPORTER_ASSERT(reporter, devClip.height() == LAYER_HEIGHT);
    343     canvas.restore();
    344 }
    345