Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2012 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 "TestClassDef.h"
     10 #include "SkBitmapDevice.h"
     11 #include "SkCanvas.h"
     12 #include "SkColorPriv.h"
     13 #include "SkData.h"
     14 #include "SkDecodingImageGenerator.h"
     15 #include "SkError.h"
     16 #include "SkPaint.h"
     17 #include "SkPicture.h"
     18 #include "SkPictureUtils.h"
     19 #include "SkRandom.h"
     20 #include "SkRRect.h"
     21 #include "SkShader.h"
     22 #include "SkStream.h"
     23 
     24 
     25 static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
     26     bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
     27     bm->allocPixels();
     28     bm->eraseColor(color);
     29     if (immutable) {
     30         bm->setImmutable();
     31     }
     32 }
     33 
     34 typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
     35 
     36 static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
     37                             const SkPoint& pos) {
     38     canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
     39 }
     40 
     41 static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
     42                                 const SkPoint& pos) {
     43     SkRect r = {
     44         0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
     45     };
     46     r.offset(pos.fX, pos.fY);
     47     canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
     48 }
     49 
     50 static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
     51                             const SkPoint& pos) {
     52     SkRect r = {
     53         0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
     54     };
     55     r.offset(pos.fX, pos.fY);
     56 
     57     SkShader* s = SkShader::CreateBitmapShader(bm,
     58                                                SkShader::kClamp_TileMode,
     59                                                SkShader::kClamp_TileMode);
     60     SkPaint paint;
     61     paint.setShader(s)->unref();
     62     canvas->drawRect(r, paint);
     63     canvas->drawOval(r, paint);
     64     SkRRect rr;
     65     rr.setRectXY(r, 10, 10);
     66     canvas->drawRRect(rr, paint);
     67 }
     68 
     69 // Return a picture with the bitmaps drawn at the specified positions.
     70 static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
     71                                  int count, DrawBitmapProc proc) {
     72     SkPicture* pic = new SkPicture;
     73     SkCanvas* canvas = pic->beginRecording(1000, 1000);
     74     for (int i = 0; i < count; ++i) {
     75         proc(canvas, bm[i], pos[i]);
     76     }
     77     pic->endRecording();
     78     return pic;
     79 }
     80 
     81 static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
     82     rect->fLeft   = rand.nextRangeScalar(-W, 2*W);
     83     rect->fTop    = rand.nextRangeScalar(-H, 2*H);
     84     rect->fRight  = rect->fLeft + rand.nextRangeScalar(0, W);
     85     rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
     86 
     87     // we integralize rect to make our tests more predictable, since Gather is
     88     // a little sloppy.
     89     SkIRect ir;
     90     rect->round(&ir);
     91     rect->set(ir);
     92 }
     93 
     94 // Allocate result to be large enough to hold subset, and then draw the picture
     95 // into it, offsetting by subset's top/left corner.
     96 static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
     97     SkIRect ir;
     98     subset.roundOut(&ir);
     99     int w = ir.width();
    100     int h = ir.height();
    101     make_bm(result, w, h, 0, false);
    102 
    103     SkCanvas canvas(*result);
    104     canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
    105     canvas.drawPicture(*pic);
    106 }
    107 
    108 template <typename T> int find_index(const T* array, T elem, int count) {
    109     for (int i = 0; i < count; ++i) {
    110         if (array[i] == elem) {
    111             return i;
    112         }
    113     }
    114     return -1;
    115 }
    116 
    117 // Return true if 'ref' is found in array[]
    118 static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
    119     return find_index<const SkPixelRef*>(array, ref, count) >= 0;
    120 }
    121 
    122 // Look at each pixel in bm, and if its color appears in colors[], find the
    123 // corresponding value in refs[] and append that ref into array, skipping
    124 // duplicates of the same value.
    125 static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
    126                                int count, SkTDArray<SkPixelRef*>* array) {
    127     // Since we only want to return unique values in array, when we scan we just
    128     // set a bit for each index'd color found. In practice we only have a few
    129     // distinct colors, so we just use an int's bits as our array. Hence the
    130     // assert that count <= number-of-bits-in-our-int.
    131     SkASSERT((unsigned)count <= 32);
    132     uint32_t bitarray = 0;
    133 
    134     SkAutoLockPixels alp(bm);
    135 
    136     for (int y = 0; y < bm.height(); ++y) {
    137         for (int x = 0; x < bm.width(); ++x) {
    138             SkPMColor pmc = *bm.getAddr32(x, y);
    139             // the only good case where the color is not found would be if
    140             // the color is transparent, meaning no bitmap was drawn in that
    141             // pixel.
    142             if (pmc) {
    143                 uint32_t index = SkGetPackedR32(pmc);
    144                 SkASSERT(SkGetPackedG32(pmc) == index);
    145                 SkASSERT(SkGetPackedB32(pmc) == index);
    146                 SkASSERT(static_cast<int>(index) < count);
    147                 bitarray |= 1 << index;
    148             }
    149         }
    150     }
    151 
    152     for (int i = 0; i < count; ++i) {
    153         if (bitarray & (1 << i)) {
    154             *array->append() = refs[i];
    155         }
    156     }
    157 }
    158 
    159 static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
    160     const int IW = 8;
    161     const int IH = IW;
    162     const SkScalar W = SkIntToScalar(IW);
    163     const SkScalar H = W;
    164 
    165     static const int N = 4;
    166     SkBitmap bm[N];
    167     SkPixelRef* refs[N];
    168 
    169     const SkPoint pos[] = {
    170         { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
    171     };
    172 
    173     // Our convention is that the color components contain the index of their
    174     // corresponding bitmap/pixelref
    175     for (int i = 0; i < N; ++i) {
    176         make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
    177         refs[i] = bm[i].pixelRef();
    178     }
    179 
    180     static const DrawBitmapProc procs[] = {
    181         drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
    182     };
    183 
    184     SkRandom rand;
    185     for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
    186         SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
    187 
    188         REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
    189         // quick check for a small piece of each quadrant, which should just
    190         // contain 1 bitmap.
    191         for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
    192             SkRect r;
    193             r.set(2, 2, W - 2, H - 2);
    194             r.offset(pos[i].fX, pos[i].fY);
    195             SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
    196             REPORTER_ASSERT(reporter, data);
    197             if (data) {
    198                 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*));
    199                 REPORTER_ASSERT(reporter, 1 == count);
    200                 REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
    201             }
    202         }
    203 
    204         // Test a bunch of random (mostly) rects, and compare the gather results
    205         // with a deduced list of refs by looking at the colors drawn.
    206         for (int j = 0; j < 100; ++j) {
    207             SkRect r;
    208             rand_rect(&r, rand, 2*W, 2*H);
    209 
    210             SkBitmap result;
    211             draw(pic, r, &result);
    212             SkTDArray<SkPixelRef*> array;
    213 
    214             SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
    215             size_t dataSize = data ? data->size() : 0;
    216             int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*));
    217             SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
    218             SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
    219             SkAutoDataUnref adu(data);
    220 
    221             gather_from_colors(result, refs, N, &array);
    222 
    223             /*
    224              *  GatherPixelRefs is conservative, so it can return more bitmaps
    225              *  that we actually can see (usually because of conservative bounds
    226              *  inflation for antialiasing). Thus our check here is only that
    227              *  Gather didn't miss any that we actually saw. Even that isn't
    228              *  a strict requirement on Gather, which is meant to be quick and
    229              *  only mostly-correct, but at the moment this test should work.
    230              */
    231             for (int i = 0; i < array.count(); ++i) {
    232                 bool found = find(gatherRefs, array[i], gatherCount);
    233                 REPORTER_ASSERT(reporter, found);
    234 #if 0
    235                 // enable this block of code to debug failures, as it will rerun
    236                 // the case that failed.
    237                 if (!found) {
    238                     SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
    239                     size_t dataSize = data ? data->size() : 0;
    240                 }
    241 #endif
    242             }
    243         }
    244     }
    245 }
    246 
    247 #ifdef SK_DEBUG
    248 // Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
    249 // run in debug mode.
    250 static void test_deleting_empty_playback() {
    251     SkPicture picture;
    252     // Creates an SkPictureRecord
    253     picture.beginRecording(0, 0);
    254     // Turns that into an SkPicturePlayback
    255     picture.endRecording();
    256     // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
    257     picture.beginRecording(0, 0);
    258 }
    259 
    260 // Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
    261 static void test_serializing_empty_picture() {
    262     SkPicture picture;
    263     picture.beginRecording(0, 0);
    264     picture.endRecording();
    265     SkDynamicMemoryWStream stream;
    266     picture.serialize(&stream);
    267 }
    268 #endif
    269 
    270 static void rand_op(SkCanvas* canvas, SkRandom& rand) {
    271     SkPaint paint;
    272     SkRect rect = SkRect::MakeWH(50, 50);
    273 
    274     SkScalar unit = rand.nextUScalar1();
    275     if (unit <= 0.3) {
    276 //        SkDebugf("save\n");
    277         canvas->save();
    278     } else if (unit <= 0.6) {
    279 //        SkDebugf("restore\n");
    280         canvas->restore();
    281     } else if (unit <= 0.9) {
    282 //        SkDebugf("clip\n");
    283         canvas->clipRect(rect);
    284     } else {
    285 //        SkDebugf("draw\n");
    286         canvas->drawPaint(paint);
    287     }
    288 }
    289 
    290 static void test_peephole() {
    291     SkRandom rand;
    292 
    293     for (int j = 0; j < 100; j++) {
    294         SkRandom rand2(rand); // remember the seed
    295 
    296         SkPicture picture;
    297         SkCanvas* canvas = picture.beginRecording(100, 100);
    298 
    299         for (int i = 0; i < 1000; ++i) {
    300             rand_op(canvas, rand);
    301         }
    302         picture.endRecording();
    303 
    304         rand = rand2;
    305     }
    306 
    307     {
    308         SkPicture picture;
    309         SkCanvas* canvas = picture.beginRecording(100, 100);
    310         SkRect rect = SkRect::MakeWH(50, 50);
    311 
    312         for (int i = 0; i < 100; ++i) {
    313             canvas->save();
    314         }
    315         while (canvas->getSaveCount() > 1) {
    316             canvas->clipRect(rect);
    317             canvas->restore();
    318         }
    319         picture.endRecording();
    320     }
    321 }
    322 
    323 #ifndef SK_DEBUG
    324 // Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
    325 // should never do this.
    326 static void test_bad_bitmap() {
    327     // This bitmap has a width and height but no pixels. As a result, attempting to record it will
    328     // fail.
    329     SkBitmap bm;
    330     bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
    331     SkPicture picture;
    332     SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
    333     recordingCanvas->drawBitmap(bm, 0, 0);
    334     picture.endRecording();
    335 
    336     SkCanvas canvas;
    337     canvas.drawPicture(picture);
    338 }
    339 #endif
    340 
    341 #include "SkImageEncoder.h"
    342 
    343 static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) {
    344     *offset = 0;
    345     return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
    346 }
    347 
    348 static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
    349     SkPicture picture;
    350     SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
    351     canvas->drawBitmap(bitmap, 0, 0);
    352     SkDynamicMemoryWStream wStream;
    353     picture.serialize(&wStream, &encode_bitmap_to_data);
    354     return wStream.copyToData();
    355 }
    356 
    357 struct ErrorContext {
    358     int fErrors;
    359     skiatest::Reporter* fReporter;
    360 };
    361 
    362 static void assert_one_parse_error_cb(SkError error, void* context) {
    363     ErrorContext* errorContext = static_cast<ErrorContext*>(context);
    364     errorContext->fErrors++;
    365     // This test only expects one error, and that is a kParseError. If there are others,
    366     // there is some unknown problem.
    367     REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
    368                             "This threw more errors than expected.");
    369     REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
    370                             SkGetLastErrorString());
    371 }
    372 
    373 static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
    374     // Create a bitmap that will be encoded.
    375     SkBitmap original;
    376     make_bm(&original, 100, 100, SK_ColorBLUE, true);
    377     SkDynamicMemoryWStream wStream;
    378     if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
    379         return;
    380     }
    381     SkAutoDataUnref data(wStream.copyToData());
    382 
    383     SkBitmap bm;
    384     bool installSuccess = SkDecodingImageGenerator::Install(data, &bm);
    385     REPORTER_ASSERT(reporter, installSuccess);
    386 
    387     // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
    388     // Flattening original will follow the old path of performing an encode, while flattening bm
    389     // will use the already encoded data.
    390     SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
    391     SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
    392     REPORTER_ASSERT(reporter, picture1->equals(picture2));
    393     // Now test that a parse error was generated when trying to create a new SkPicture without
    394     // providing a function to decode the bitmap.
    395     ErrorContext context;
    396     context.fErrors = 0;
    397     context.fReporter = reporter;
    398     SkSetErrorCallback(assert_one_parse_error_cb, &context);
    399     SkMemoryStream pictureStream(picture1);
    400     SkClearLastError();
    401     SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
    402     REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
    403     SkClearLastError();
    404     SkSetErrorCallback(NULL, NULL);
    405 }
    406 
    407 static void test_clone_empty(skiatest::Reporter* reporter) {
    408     // This is a regression test for crbug.com/172062
    409     // Before the fix, we used to crash accessing a null pointer when we
    410     // had a picture with no paints. This test passes by not crashing.
    411     {
    412         SkPicture picture;
    413         picture.beginRecording(1, 1);
    414         picture.endRecording();
    415         SkPicture* destPicture = picture.clone();
    416         REPORTER_ASSERT(reporter, NULL != destPicture);
    417         destPicture->unref();
    418     }
    419     {
    420         // Test without call to endRecording
    421         SkPicture picture;
    422         picture.beginRecording(1, 1);
    423         SkPicture* destPicture = picture.clone();
    424         REPORTER_ASSERT(reporter, NULL != destPicture);
    425         destPicture->unref();
    426     }
    427 }
    428 
    429 static void test_clip_bound_opt(skiatest::Reporter* reporter) {
    430     // Test for crbug.com/229011
    431     SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
    432                                     SkIntToScalar(2), SkIntToScalar(2));
    433     SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
    434                                     SkIntToScalar(1), SkIntToScalar(1));
    435     SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
    436                                     SkIntToScalar(1), SkIntToScalar(1));
    437 
    438     SkPath invPath;
    439     invPath.addOval(rect1);
    440     invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
    441     SkPath path;
    442     path.addOval(rect2);
    443     SkPath path2;
    444     path2.addOval(rect3);
    445     SkIRect clipBounds;
    446     // Minimalist test set for 100% code coverage of
    447     // SkPictureRecord::updateClipConservativelyUsingBounds
    448     {
    449         SkPicture picture;
    450         SkCanvas* canvas = picture.beginRecording(10, 10,
    451             SkPicture::kUsePathBoundsForClip_RecordingFlag);
    452         canvas->clipPath(invPath, SkRegion::kIntersect_Op);
    453         bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
    454         REPORTER_ASSERT(reporter, true == nonEmpty);
    455         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
    456         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
    457         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
    458         REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
    459     }
    460     {
    461         SkPicture picture;
    462         SkCanvas* canvas = picture.beginRecording(10, 10,
    463             SkPicture::kUsePathBoundsForClip_RecordingFlag);
    464         canvas->clipPath(path, SkRegion::kIntersect_Op);
    465         canvas->clipPath(invPath, SkRegion::kIntersect_Op);
    466         bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
    467         REPORTER_ASSERT(reporter, true == nonEmpty);
    468         REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
    469         REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
    470         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
    471         REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
    472     }
    473     {
    474         SkPicture picture;
    475         SkCanvas* canvas = picture.beginRecording(10, 10,
    476             SkPicture::kUsePathBoundsForClip_RecordingFlag);
    477         canvas->clipPath(path, SkRegion::kIntersect_Op);
    478         canvas->clipPath(invPath, SkRegion::kUnion_Op);
    479         bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
    480         REPORTER_ASSERT(reporter, true == nonEmpty);
    481         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
    482         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
    483         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
    484         REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
    485     }
    486     {
    487         SkPicture picture;
    488         SkCanvas* canvas = picture.beginRecording(10, 10,
    489             SkPicture::kUsePathBoundsForClip_RecordingFlag);
    490         canvas->clipPath(path, SkRegion::kDifference_Op);
    491         bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
    492         REPORTER_ASSERT(reporter, true == nonEmpty);
    493         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
    494         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
    495         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
    496         REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
    497     }
    498     {
    499         SkPicture picture;
    500         SkCanvas* canvas = picture.beginRecording(10, 10,
    501             SkPicture::kUsePathBoundsForClip_RecordingFlag);
    502         canvas->clipPath(path, SkRegion::kReverseDifference_Op);
    503         bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
    504         // True clip is actually empty in this case, but the best
    505         // determination we can make using only bounds as input is that the
    506         // clip is included in the bounds of 'path'.
    507         REPORTER_ASSERT(reporter, true == nonEmpty);
    508         REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
    509         REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
    510         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
    511         REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
    512     }
    513     {
    514         SkPicture picture;
    515         SkCanvas* canvas = picture.beginRecording(10, 10,
    516             SkPicture::kUsePathBoundsForClip_RecordingFlag);
    517         canvas->clipPath(path, SkRegion::kIntersect_Op);
    518         canvas->clipPath(path2, SkRegion::kXOR_Op);
    519         bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
    520         REPORTER_ASSERT(reporter, true == nonEmpty);
    521         REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
    522         REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
    523         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
    524         REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
    525     }
    526 }
    527 
    528 /**
    529  * A canvas that records the number of clip commands.
    530  */
    531 class ClipCountingCanvas : public SkCanvas {
    532 public:
    533     explicit ClipCountingCanvas(SkBaseDevice* device)
    534         : SkCanvas(device)
    535         , fClipCount(0){
    536     }
    537 
    538     virtual bool clipRect(const SkRect& r, SkRegion::Op op, bool doAA)
    539         SK_OVERRIDE {
    540         fClipCount += 1;
    541         return this->INHERITED::clipRect(r, op, doAA);
    542     }
    543 
    544     virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA)
    545         SK_OVERRIDE {
    546         fClipCount += 1;
    547         return this->INHERITED::clipRRect(rrect, op, doAA);
    548     }
    549 
    550     virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA)
    551         SK_OVERRIDE {
    552         fClipCount += 1;
    553         return this->INHERITED::clipPath(path, op, doAA);
    554     }
    555 
    556     unsigned getClipCount() const { return fClipCount; }
    557 
    558 private:
    559     unsigned fClipCount;
    560 
    561     typedef SkCanvas INHERITED;
    562 };
    563 
    564 static void test_clip_expansion(skiatest::Reporter* reporter) {
    565     SkPicture picture;
    566     SkCanvas* canvas = picture.beginRecording(10, 10, 0);
    567 
    568     canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
    569     // The following expanding clip should not be skipped.
    570     canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
    571     // Draw something so the optimizer doesn't just fold the world.
    572     SkPaint p;
    573     p.setColor(SK_ColorBLUE);
    574     canvas->drawPaint(p);
    575 
    576     SkBitmapDevice testDevice(SkBitmap::kNo_Config, 10, 10);
    577     ClipCountingCanvas testCanvas(&testDevice);
    578     picture.draw(&testCanvas);
    579 
    580     // Both clips should be present on playback.
    581     REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
    582 }
    583 
    584 static void test_hierarchical(skiatest::Reporter* reporter) {
    585     SkBitmap bm;
    586     make_bm(&bm, 10, 10, SK_ColorRED, true);
    587 
    588     SkCanvas* canvas;
    589 
    590     SkPicture childPlain;
    591     childPlain.beginRecording(10, 10);
    592     childPlain.endRecording();
    593     REPORTER_ASSERT(reporter, !childPlain.willPlayBackBitmaps()); // 0
    594 
    595     SkPicture childWithBitmap;
    596     childWithBitmap.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
    597     childWithBitmap.endRecording();
    598     REPORTER_ASSERT(reporter, childWithBitmap.willPlayBackBitmaps()); // 1
    599 
    600     SkPicture parentPP;
    601     canvas = parentPP.beginRecording(10, 10);
    602     canvas->drawPicture(childPlain);
    603     parentPP.endRecording();
    604     REPORTER_ASSERT(reporter, !parentPP.willPlayBackBitmaps()); // 0
    605 
    606     SkPicture parentPWB;
    607     canvas = parentPWB.beginRecording(10, 10);
    608     canvas->drawPicture(childWithBitmap);
    609     parentPWB.endRecording();
    610     REPORTER_ASSERT(reporter, parentPWB.willPlayBackBitmaps()); // 1
    611 
    612     SkPicture parentWBP;
    613     canvas = parentWBP.beginRecording(10, 10);
    614     canvas->drawBitmap(bm, 0, 0);
    615     canvas->drawPicture(childPlain);
    616     parentWBP.endRecording();
    617     REPORTER_ASSERT(reporter, parentWBP.willPlayBackBitmaps()); // 1
    618 
    619     SkPicture parentWBWB;
    620     canvas = parentWBWB.beginRecording(10, 10);
    621     canvas->drawBitmap(bm, 0, 0);
    622     canvas->drawPicture(childWithBitmap);
    623     parentWBWB.endRecording();
    624     REPORTER_ASSERT(reporter, parentWBWB.willPlayBackBitmaps()); // 2
    625 }
    626 
    627 DEF_TEST(Picture, reporter) {
    628 #ifdef SK_DEBUG
    629     test_deleting_empty_playback();
    630     test_serializing_empty_picture();
    631 #else
    632     test_bad_bitmap();
    633 #endif
    634     test_peephole();
    635     test_gatherpixelrefs(reporter);
    636     test_bitmap_with_encoded_data(reporter);
    637     test_clone_empty(reporter);
    638     test_clip_bound_opt(reporter);
    639     test_clip_expansion(reporter);
    640     test_hierarchical(reporter);
    641 }
    642