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 #include "Test.h"
      8 #include "SkCanvas.h"
      9 #include "SkColorPriv.h"
     10 #include "SkData.h"
     11 #include "SkPaint.h"
     12 #include "SkPicture.h"
     13 #include "SkRandom.h"
     14 #include "SkRRect.h"
     15 #include "SkShader.h"
     16 #include "SkStream.h"
     17 
     18 #include "SkPictureUtils.h"
     19 
     20 static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
     21     bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
     22     bm->allocPixels();
     23     bm->eraseColor(color);
     24     if (immutable) {
     25         bm->setImmutable();
     26     }
     27 }
     28 
     29 typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
     30 
     31 static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
     32                             const SkPoint& pos) {
     33     canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
     34 }
     35 
     36 static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
     37                                 const SkPoint& pos) {
     38     SkRect r = {
     39         0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
     40     };
     41     r.offset(pos.fX, pos.fY);
     42     canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
     43 }
     44 
     45 static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
     46                             const SkPoint& pos) {
     47     SkRect r = {
     48         0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
     49     };
     50     r.offset(pos.fX, pos.fY);
     51 
     52     SkShader* s = SkShader::CreateBitmapShader(bm,
     53                                                SkShader::kClamp_TileMode,
     54                                                SkShader::kClamp_TileMode);
     55     SkPaint paint;
     56     paint.setShader(s)->unref();
     57     canvas->drawRect(r, paint);
     58     canvas->drawOval(r, paint);
     59     SkRRect rr;
     60     rr.setRectXY(r, 10, 10);
     61     canvas->drawRRect(rr, paint);
     62 }
     63 
     64 // Return a picture with the bitmaps drawn at the specified positions.
     65 static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
     66                                  int count, DrawBitmapProc proc) {
     67     SkPicture* pic = new SkPicture;
     68     SkCanvas* canvas = pic->beginRecording(1000, 1000);
     69     for (int i = 0; i < count; ++i) {
     70         proc(canvas, bm[i], pos[i]);
     71     }
     72     pic->endRecording();
     73     return pic;
     74 }
     75 
     76 static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
     77     rect->fLeft   = rand.nextRangeScalar(-W, 2*W);
     78     rect->fTop    = rand.nextRangeScalar(-H, 2*H);
     79     rect->fRight  = rect->fLeft + rand.nextRangeScalar(0, W);
     80     rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
     81 
     82     // we integralize rect to make our tests more predictable, since Gather is
     83     // a little sloppy.
     84     SkIRect ir;
     85     rect->round(&ir);
     86     rect->set(ir);
     87 }
     88 
     89 // Allocate result to be large enough to hold subset, and then draw the picture
     90 // into it, offsetting by subset's top/left corner.
     91 static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
     92     SkIRect ir;
     93     subset.roundOut(&ir);
     94     int w = ir.width();
     95     int h = ir.height();
     96     make_bm(result, w, h, 0, false);
     97 
     98     SkCanvas canvas(*result);
     99     canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
    100     canvas.drawPicture(*pic);
    101 }
    102 
    103 template <typename T> int find_index(const T* array, T elem, int count) {
    104     for (int i = 0; i < count; ++i) {
    105         if (array[i] == elem) {
    106             return i;
    107         }
    108     }
    109     return -1;
    110 }
    111 
    112 // Return true if 'ref' is found in array[]
    113 static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
    114     return find_index<const SkPixelRef*>(array, ref, count) >= 0;
    115 }
    116 
    117 // Look at each pixel in bm, and if its color appears in colors[], find the
    118 // corresponding value in refs[] and append that ref into array, skipping
    119 // duplicates of the same value.
    120 static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
    121                                int count, SkTDArray<SkPixelRef*>* array) {
    122     // Since we only want to return unique values in array, when we scan we just
    123     // set a bit for each index'd color found. In practice we only have a few
    124     // distinct colors, so we just use an int's bits as our array. Hence the
    125     // assert that count <= number-of-bits-in-our-int.
    126     SkASSERT((unsigned)count <= 32);
    127     uint32_t bitarray = 0;
    128 
    129     SkAutoLockPixels alp(bm);
    130 
    131     for (int y = 0; y < bm.height(); ++y) {
    132         for (int x = 0; x < bm.width(); ++x) {
    133             SkPMColor pmc = *bm.getAddr32(x, y);
    134             // the only good case where the color is not found would be if
    135             // the color is transparent, meaning no bitmap was drawn in that
    136             // pixel.
    137             if (pmc) {
    138                 uint32_t index = SkGetPackedR32(pmc);
    139                 SkASSERT(SkGetPackedG32(pmc) == index);
    140                 SkASSERT(SkGetPackedB32(pmc) == index);
    141                 SkASSERT(static_cast<int>(index) < count);
    142                 bitarray |= 1 << index;
    143             }
    144         }
    145     }
    146 
    147     for (int i = 0; i < count; ++i) {
    148         if (bitarray & (1 << i)) {
    149             *array->append() = refs[i];
    150         }
    151     }
    152 }
    153 
    154 static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
    155     const int IW = 8;
    156     const int IH = IW;
    157     const SkScalar W = SkIntToScalar(IW);
    158     const SkScalar H = W;
    159 
    160     static const int N = 4;
    161     SkBitmap bm[N];
    162     SkPixelRef* refs[N];
    163 
    164     const SkPoint pos[] = {
    165         { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
    166     };
    167 
    168     // Our convention is that the color components contain the index of their
    169     // corresponding bitmap/pixelref
    170     for (int i = 0; i < N; ++i) {
    171         make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
    172         refs[i] = bm[i].pixelRef();
    173     }
    174 
    175     static const DrawBitmapProc procs[] = {
    176         drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
    177     };
    178 
    179     SkRandom rand;
    180     for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
    181         SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
    182 
    183         // quick check for a small piece of each quadrant, which should just
    184         // contain 1 bitmap.
    185         for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
    186             SkRect r;
    187             r.set(2, 2, W - 2, H - 2);
    188             r.offset(pos[i].fX, pos[i].fY);
    189             SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
    190             REPORTER_ASSERT(reporter, data);
    191             int count = data->size() / sizeof(SkPixelRef*);
    192             REPORTER_ASSERT(reporter, 1 == count);
    193             REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
    194         }
    195 
    196         // Test a bunch of random (mostly) rects, and compare the gather results
    197         // with a deduced list of refs by looking at the colors drawn.
    198         for (int j = 0; j < 100; ++j) {
    199             SkRect r;
    200             rand_rect(&r, rand, 2*W, 2*H);
    201 
    202             SkBitmap result;
    203             draw(pic, r, &result);
    204             SkTDArray<SkPixelRef*> array;
    205 
    206             SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
    207             size_t dataSize = data ? data->size() : 0;
    208             int gatherCount = dataSize / sizeof(SkPixelRef*);
    209             SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
    210             SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
    211             SkAutoDataUnref adu(data);
    212 
    213             gather_from_colors(result, refs, N, &array);
    214 
    215             /*
    216              *  GatherPixelRefs is conservative, so it can return more bitmaps
    217              *  that we actually can see (usually because of conservative bounds
    218              *  inflation for antialiasing). Thus our check here is only that
    219              *  Gather didn't miss any that we actually saw. Even that isn't
    220              *  a strict requirement on Gather, which is meant to be quick and
    221              *  only mostly-correct, but at the moment this test should work.
    222              */
    223             for (int i = 0; i < array.count(); ++i) {
    224                 bool found = find(gatherRefs, array[i], gatherCount);
    225                 REPORTER_ASSERT(reporter, found);
    226 #if 0
    227                 // enable this block of code to debug failures, as it will rerun
    228                 // the case that failed.
    229                 if (!found) {
    230                     SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
    231                     size_t dataSize = data ? data->size() : 0;
    232                 }
    233 #endif
    234             }
    235         }
    236     }
    237 }
    238 
    239 #ifdef SK_DEBUG
    240 // Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
    241 // run in debug mode.
    242 static void test_deleting_empty_playback() {
    243     SkPicture picture;
    244     // Creates an SkPictureRecord
    245     picture.beginRecording(0, 0);
    246     // Turns that into an SkPicturePlayback
    247     picture.endRecording();
    248     // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
    249     picture.beginRecording(0, 0);
    250 }
    251 
    252 // Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
    253 static void test_serializing_empty_picture() {
    254     SkPicture picture;
    255     picture.beginRecording(0, 0);
    256     picture.endRecording();
    257     SkDynamicMemoryWStream stream;
    258     picture.serialize(&stream);
    259 }
    260 #endif
    261 
    262 static void rand_op(SkCanvas* canvas, SkRandom& rand) {
    263     SkPaint paint;
    264     SkRect rect = SkRect::MakeWH(50, 50);
    265 
    266     SkScalar unit = rand.nextUScalar1();
    267     if (unit <= 0.3) {
    268 //        SkDebugf("save\n");
    269         canvas->save();
    270     } else if (unit <= 0.6) {
    271 //        SkDebugf("restore\n");
    272         canvas->restore();
    273     } else if (unit <= 0.9) {
    274 //        SkDebugf("clip\n");
    275         canvas->clipRect(rect);
    276     } else {
    277 //        SkDebugf("draw\n");
    278         canvas->drawPaint(paint);
    279     }
    280 }
    281 
    282 static void test_peephole(skiatest::Reporter* reporter) {
    283     SkRandom rand;
    284 
    285     for (int j = 0; j < 100; j++) {
    286         SkRandom rand2(rand.getSeed()); // remember the seed
    287 
    288         SkPicture picture;
    289         SkCanvas* canvas = picture.beginRecording(100, 100);
    290 
    291         for (int i = 0; i < 1000; ++i) {
    292             rand_op(canvas, rand);
    293         }
    294         picture.endRecording();
    295     }
    296 
    297     {
    298         SkPicture picture;
    299         SkCanvas* canvas = picture.beginRecording(100, 100);
    300         SkRect rect = SkRect::MakeWH(50, 50);
    301 
    302         for (int i = 0; i < 100; ++i) {
    303             canvas->save();
    304         }
    305         while (canvas->getSaveCount() > 1) {
    306             canvas->clipRect(rect);
    307             canvas->restore();
    308         }
    309         picture.endRecording();
    310     }
    311 }
    312 
    313 #ifndef SK_DEBUG
    314 // Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
    315 // should never do this.
    316 static void test_bad_bitmap() {
    317     // This bitmap has a width and height but no pixels. As a result, attempting to record it will
    318     // fail.
    319     SkBitmap bm;
    320     bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
    321     SkPicture picture;
    322     SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
    323     recordingCanvas->drawBitmap(bm, 0, 0);
    324     picture.endRecording();
    325 
    326     SkCanvas canvas;
    327     canvas.drawPicture(picture);
    328 }
    329 #endif
    330 
    331 #include "SkData.h"
    332 #include "SkImageRef_GlobalPool.h"
    333 // Class to test SkPixelRef::onRefEncodedData, since there are currently no implementations in skia.
    334 class SkDataImageRef : public SkImageRef_GlobalPool {
    335 
    336 public:
    337     SkDataImageRef(SkMemoryStream* stream)
    338         : SkImageRef_GlobalPool(stream, SkBitmap::kNo_Config) {
    339         SkASSERT(stream != NULL);
    340         fData = stream->copyToData();
    341         this->setImmutable();
    342     }
    343 
    344     ~SkDataImageRef() {
    345         fData->unref();
    346     }
    347 
    348     virtual SkData* onRefEncodedData() SK_OVERRIDE {
    349         fData->ref();
    350         return fData;
    351     }
    352 
    353 private:
    354     SkData* fData;
    355 };
    356 
    357 #include "SkImageEncoder.h"
    358 
    359 static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
    360     return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
    361 }
    362 
    363 static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
    364     SkPicture picture;
    365     SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
    366     canvas->drawBitmap(bitmap, 0, 0);
    367     SkDynamicMemoryWStream wStream;
    368     picture.serialize(&wStream, &PNGEncodeBitmapToStream);
    369     return wStream.copyToData();
    370 }
    371 
    372 static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
    373     // Create a bitmap that will be encoded.
    374     SkBitmap original;
    375     make_bm(&original, 100, 100, SK_ColorBLUE, true);
    376     SkDynamicMemoryWStream wStream;
    377     if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
    378         return;
    379     }
    380     SkAutoDataUnref data(wStream.copyToData());
    381     SkMemoryStream memStream;
    382     memStream.setData(data);
    383 
    384     // Use the encoded bitmap as the data for an image ref.
    385     SkBitmap bm;
    386     SkAutoTUnref<SkDataImageRef> imageRef(SkNEW_ARGS(SkDataImageRef, (&memStream)));
    387     imageRef->getInfo(&bm);
    388     bm.setPixelRef(imageRef);
    389 
    390     // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
    391     // Flattening original will follow the old path of performing an encode, while flattening bm
    392     // will use the already encoded data.
    393     SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
    394     SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
    395     REPORTER_ASSERT(reporter, picture1->equals(picture2));
    396 }
    397 
    398 static void test_clone_empty(skiatest::Reporter* reporter) {
    399     // This is a regression test for crbug.com/172062
    400     // Before the fix, we used to crash accessing a null pointer when we
    401     // had a picture with no paints. This test passes by not crashing.
    402     {
    403         SkPicture picture;
    404         picture.beginRecording(1, 1);
    405         picture.endRecording();
    406         SkPicture* destPicture = picture.clone();
    407         REPORTER_ASSERT(reporter, NULL != destPicture);
    408         destPicture->unref();
    409     }
    410     {
    411         // Test without call to endRecording
    412         SkPicture picture;
    413         picture.beginRecording(1, 1);
    414         SkPicture* destPicture = picture.clone();
    415         REPORTER_ASSERT(reporter, NULL != destPicture);
    416         destPicture->unref();
    417     }
    418 }
    419 
    420 static void TestPicture(skiatest::Reporter* reporter) {
    421 #ifdef SK_DEBUG
    422     test_deleting_empty_playback();
    423     test_serializing_empty_picture();
    424 #else
    425     test_bad_bitmap();
    426 #endif
    427     test_peephole(reporter);
    428     test_gatherpixelrefs(reporter);
    429     test_bitmap_with_encoded_data(reporter);
    430     test_clone_empty(reporter);
    431 }
    432 
    433 #include "TestClassDef.h"
    434 DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture)
    435