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 "SkDebugCanvas.h"
     12 #include "SkDrawPictureCallback.h"
     13 #include "SkDropShadowImageFilter.h"
     14 #include "SkRecord.h"
     15 #include "SkRecordDraw.h"
     16 #include "SkRecordOpts.h"
     17 #include "SkRecorder.h"
     18 #include "SkRecords.h"
     19 
     20 static const int W = 1920, H = 1080;
     21 
     22 class JustOneDraw : public SkDrawPictureCallback {
     23 public:
     24     JustOneDraw() : fCalls(0) {}
     25 
     26     virtual bool abortDrawing() SK_OVERRIDE { return fCalls++ > 0; }
     27 private:
     28     int fCalls;
     29 };
     30 
     31 DEF_TEST(RecordDraw_Abort, r) {
     32     // Record two commands.
     33     SkRecord record;
     34     SkRecorder recorder(&record, W, H);
     35     recorder.drawRect(SkRect::MakeWH(200, 300), SkPaint());
     36     recorder.clipRect(SkRect::MakeWH(100, 200));
     37 
     38     SkRecord rerecord;
     39     SkRecorder canvas(&rerecord, W, H);
     40 
     41     JustOneDraw callback;
     42     SkRecordDraw(record, &canvas, NULL/*bbh*/, &callback);
     43 
     44     REPORTER_ASSERT(r, 3 == rerecord.count());
     45     assert_type<SkRecords::Save>    (r, rerecord, 0);
     46     assert_type<SkRecords::DrawRect>(r, rerecord, 1);
     47     assert_type<SkRecords::Restore> (r, rerecord, 2);
     48 }
     49 
     50 DEF_TEST(RecordDraw_Unbalanced, r) {
     51     SkRecord record;
     52     SkRecorder recorder(&record, W, H);
     53     recorder.save();  // We won't balance this, but SkRecordDraw will for us.
     54 
     55     SkRecord rerecord;
     56     SkRecorder canvas(&rerecord, W, H);
     57     SkRecordDraw(record, &canvas, NULL/*bbh*/, NULL/*callback*/);
     58 
     59     REPORTER_ASSERT(r, 4 == rerecord.count());
     60     assert_type<SkRecords::Save>    (r, rerecord, 0);
     61     assert_type<SkRecords::Save>    (r, rerecord, 1);
     62     assert_type<SkRecords::Restore> (r, rerecord, 2);
     63     assert_type<SkRecords::Restore> (r, rerecord, 3);
     64 }
     65 
     66 DEF_TEST(RecordDraw_SetMatrixClobber, r) {
     67     // Set up an SkRecord that just scales by 2x,3x.
     68     SkRecord scaleRecord;
     69     SkRecorder scaleCanvas(&scaleRecord, W, H);
     70     SkMatrix scale;
     71     scale.setScale(2, 3);
     72     scaleCanvas.setMatrix(scale);
     73 
     74     // Set up an SkRecord with an initial +20, +20 translate.
     75     SkRecord translateRecord;
     76     SkRecorder translateCanvas(&translateRecord, W, H);
     77     SkMatrix translate;
     78     translate.setTranslate(20, 20);
     79     translateCanvas.setMatrix(translate);
     80 
     81     SkRecordDraw(scaleRecord, &translateCanvas, NULL/*bbh*/, NULL/*callback*/);
     82     REPORTER_ASSERT(r, 4 == translateRecord.count());
     83     assert_type<SkRecords::SetMatrix>(r, translateRecord, 0);
     84     assert_type<SkRecords::Save>     (r, translateRecord, 1);
     85     assert_type<SkRecords::SetMatrix>(r, translateRecord, 2);
     86     assert_type<SkRecords::Restore>  (r, translateRecord, 3);
     87 
     88     // When we look at translateRecord now, it should have its first +20,+20 translate,
     89     // then a 2x,3x scale that's been concatted with that +20,+20 translate.
     90     const SkRecords::SetMatrix* setMatrix;
     91     setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 0);
     92     REPORTER_ASSERT(r, setMatrix->matrix == translate);
     93 
     94     setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 2);
     95     SkMatrix expected = scale;
     96     expected.postConcat(translate);
     97     REPORTER_ASSERT(r, setMatrix->matrix == expected);
     98 }
     99 
    100 struct TestBBH : public SkBBoxHierarchy {
    101     virtual void insert(void* data, const SkRect& bounds, bool defer) SK_OVERRIDE {
    102         Entry e = { (uintptr_t)data, bounds };
    103         entries.push(e);
    104     }
    105     virtual int getCount() const SK_OVERRIDE { return entries.count(); }
    106 
    107     virtual void flushDeferredInserts() SK_OVERRIDE {}
    108 
    109     virtual void search(const SkRect& query, SkTDArray<void*>* results) const SK_OVERRIDE {}
    110     virtual void clear() SK_OVERRIDE {}
    111     virtual void rewindInserts() SK_OVERRIDE {}
    112     virtual int getDepth() const SK_OVERRIDE { return -1; }
    113 
    114     struct Entry {
    115         uintptr_t data;
    116         SkRect bounds;
    117     };
    118     SkTDArray<Entry> entries;
    119 };
    120 
    121 // Like a==b, with a little slop recognizing that float equality can be weird.
    122 static bool sloppy_rect_eq(SkRect a, SkRect b) {
    123     SkRect inset(a), outset(a);
    124     inset.inset(1, 1);
    125     outset.outset(1, 1);
    126     return outset.contains(b) && !inset.contains(b);
    127 }
    128 
    129 // This test is not meant to make total sense yet.  It's testing the status quo
    130 // of SkRecordFillBounds(), which itself doesn't make total sense yet.
    131 DEF_TEST(RecordDraw_BBH, r) {
    132     SkRecord record;
    133     SkRecorder recorder(&record, W, H);
    134     recorder.save();
    135         recorder.clipRect(SkRect::MakeWH(400, 500));
    136         recorder.scale(2, 2);
    137         recorder.drawRect(SkRect::MakeWH(320, 240), SkPaint());
    138     recorder.restore();
    139 
    140     TestBBH bbh;
    141     SkRecordFillBounds(record, &bbh);
    142 
    143     REPORTER_ASSERT(r, bbh.entries.count() == 5);
    144     for (int i = 0; i < bbh.entries.count(); i++) {
    145         REPORTER_ASSERT(r, bbh.entries[i].data == (uintptr_t)i);
    146 
    147         REPORTER_ASSERT(r, sloppy_rect_eq(SkRect::MakeWH(400, 480), bbh.entries[i].bounds));
    148     }
    149 }
    150 
    151 // A regression test for crbug.com/409110.
    152 DEF_TEST(RecordDraw_TextBounds, r) {
    153     SkRecord record;
    154     SkRecorder recorder(&record, W, H);
    155 
    156     // Two Chinese characters in UTF-8.
    157     const char text[] = { '\xe6', '\xbc', '\xa2', '\xe5', '\xad', '\x97' };
    158     const size_t bytes = SK_ARRAY_COUNT(text);
    159 
    160     const SkScalar xpos[] = { 10, 20 };
    161     recorder.drawPosTextH(text, bytes, xpos, 30, SkPaint());
    162 
    163     const SkPoint pos[] = { {40, 50}, {60, 70} };
    164     recorder.drawPosText(text, bytes, pos, SkPaint());
    165 
    166     TestBBH bbh;
    167     SkRecordFillBounds(record, &bbh);
    168     REPORTER_ASSERT(r, bbh.entries.count() == 2);
    169 
    170     // We can make these next assertions confidently because SkRecordFillBounds
    171     // builds its bounds by overestimating font metrics in a platform-independent way.
    172     // If that changes, these tests will need to be more flexible.
    173     REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[0].bounds, SkRect::MakeLTRB(-86,  6, 116, 54)));
    174     REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[1].bounds, SkRect::MakeLTRB(-56, 26, 156, 94)));
    175 }
    176 
    177 // Base test to ensure start/stop range is respected
    178 DEF_TEST(RecordDraw_PartialStartStop, r) {
    179     static const int kWidth = 10, kHeight = 10;
    180 
    181     SkRect r1 = { 0, 0, kWidth,   kHeight };
    182     SkRect r2 = { 0, 0, kWidth,   kHeight/2 };
    183     SkRect r3 = { 0, 0, kWidth/2, kHeight };
    184     SkPaint p;
    185 
    186     SkRecord record;
    187     SkRecorder recorder(&record, kWidth, kHeight);
    188     recorder.drawRect(r1, p);
    189     recorder.drawRect(r2, p);
    190     recorder.drawRect(r3, p);
    191 
    192     SkRecord rerecord;
    193     SkRecorder canvas(&rerecord, kWidth, kHeight);
    194     SkRecordPartialDraw(record, &canvas, r1, 1, 2, SkMatrix::I()); // replay just drawRect of r2
    195 
    196     REPORTER_ASSERT(r, 3 == rerecord.count());
    197     assert_type<SkRecords::Save>     (r, rerecord, 0);
    198     assert_type<SkRecords::DrawRect> (r, rerecord, 1);
    199     assert_type<SkRecords::Restore>  (r, rerecord, 2);
    200 
    201     const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, 1);
    202     REPORTER_ASSERT(r, drawRect->rect == r2);
    203 }
    204 
    205 // Check that clears are converted to drawRects
    206 DEF_TEST(RecordDraw_PartialClear, r) {
    207     static const int kWidth = 10, kHeight = 10;
    208 
    209     SkRect rect = { 0, 0, kWidth, kHeight };
    210 
    211     SkRecord record;
    212     SkRecorder recorder(&record, kWidth, kHeight);
    213     recorder.clear(SK_ColorRED);
    214 
    215     SkRecord rerecord;
    216     SkRecorder canvas(&rerecord, kWidth, kHeight);
    217     SkRecordPartialDraw(record, &canvas, rect, 0, 1, SkMatrix::I()); // replay just the clear
    218 
    219     REPORTER_ASSERT(r, 3 == rerecord.count());
    220     assert_type<SkRecords::Save>    (r, rerecord, 0);
    221     assert_type<SkRecords::DrawRect>(r, rerecord, 1);
    222     assert_type<SkRecords::Restore> (r, rerecord, 2);
    223 
    224     const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, 1);
    225     REPORTER_ASSERT(r, drawRect->rect == rect);
    226     REPORTER_ASSERT(r, drawRect->paint.getColor() == SK_ColorRED);
    227 }
    228 
    229 // A regression test for crbug.com/415468 and skbug.com/2957.
    230 //
    231 // This also now serves as a regression test for crbug.com/418417.  We used to adjust the
    232 // bounds for the saveLayer, clip, and restore to be greater than the bounds of the picture.
    233 // (We were applying the saveLayer paint to the bounds after restore, which makes no sense.)
    234 DEF_TEST(RecordDraw_SaveLayerAffectsClipBounds, r) {
    235     SkRecord record;
    236     SkRecorder recorder(&record, 50, 50);
    237 
    238     // We draw a rectangle with a long drop shadow.  We used to not update the clip
    239     // bounds based on SaveLayer paints, so the drop shadow could be cut off.
    240     SkPaint paint;
    241     paint.setImageFilter(SkDropShadowImageFilter::Create(20, 0, 0, 0, SK_ColorBLACK))->unref();
    242 
    243     recorder.saveLayer(NULL, &paint);
    244         recorder.clipRect(SkRect::MakeWH(20, 40));
    245         recorder.drawRect(SkRect::MakeWH(20, 40), SkPaint());
    246     recorder.restore();
    247 
    248     // Under the original bug, the right edge value of the drawRect would be 20 less than asserted
    249     // here because we intersected it with a clip that had not been adjusted for the drop shadow.
    250     //
    251     // The second bug showed up as adjusting the picture bounds (0,0,50,50) by the drop shadow too.
    252     // The saveLayer, clipRect, and restore bounds were incorrectly (0,0,70,50).
    253     TestBBH bbh;
    254     SkRecordFillBounds(record, &bbh);
    255     REPORTER_ASSERT(r, bbh.entries.count() == 4);
    256     REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[0].bounds, SkRect::MakeLTRB(0, 0, 50, 50)));
    257     REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[1].bounds, SkRect::MakeLTRB(0, 0, 50, 50)));
    258     REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[2].bounds, SkRect::MakeLTRB(0, 0, 40, 40)));
    259     REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[3].bounds, SkRect::MakeLTRB(0, 0, 50, 50)));
    260 }
    261