RecordDrawTest.cpp revision 02d2b9831579173e783569530ab7bae08de907e9
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 "SkImagePriv.h"
15#include "SkRecord.h"
16#include "SkRecordDraw.h"
17#include "SkRecordOpts.h"
18#include "SkRecorder.h"
19#include "SkRecords.h"
20#include "SkSurface.h"
21
22static const int W = 1920, H = 1080;
23
24class JustOneDraw : public SkDrawPictureCallback {
25public:
26    JustOneDraw() : fCalls(0) {}
27
28    virtual bool abortDrawing() SK_OVERRIDE { return fCalls++ > 0; }
29private:
30    int fCalls;
31};
32
33DEF_TEST(RecordDraw_LazySaves, r) {
34    // Record two commands.
35    SkRecord record;
36    SkRecorder recorder(&record, W, H);
37
38    REPORTER_ASSERT(r, 0 == record.count());
39    recorder.save();
40    REPORTER_ASSERT(r, 0 == record.count());    // the save was not recorded (yet)
41    recorder.drawColor(SK_ColorRED);
42    REPORTER_ASSERT(r, 1 == record.count());
43    recorder.scale(2, 2);
44    REPORTER_ASSERT(r, 3 == record.count());    // now we see the save
45    recorder.restore();
46    REPORTER_ASSERT(r, 4 == record.count());
47
48    assert_type<SkRecords::DrawPaint>(r, record, 0);
49    assert_type<SkRecords::Save>     (r, record, 1);
50    assert_type<SkRecords::SetMatrix>(r, record, 2);
51    assert_type<SkRecords::Restore>  (r, record, 3);
52
53    recorder.save();
54    recorder.save();
55    recorder.restore();
56    recorder.restore();
57    REPORTER_ASSERT(r, 4 == record.count());
58}
59
60DEF_TEST(RecordDraw_Abort, r) {
61    // Record two commands.
62    SkRecord record;
63    SkRecorder recorder(&record, W, H);
64    recorder.drawRect(SkRect::MakeWH(200, 300), SkPaint());
65    recorder.clipRect(SkRect::MakeWH(100, 200));
66
67    SkRecord rerecord;
68    SkRecorder canvas(&rerecord, W, H);
69
70    JustOneDraw callback;
71    SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL/*bbh*/, &callback);
72
73    REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord));
74    REPORTER_ASSERT(r, 0 == count_instances_of_type<SkRecords::ClipRect>(rerecord));
75}
76
77DEF_TEST(RecordDraw_Unbalanced, r) {
78    SkRecord record;
79    SkRecorder recorder(&record, W, H);
80    recorder.save();  // We won't balance this, but SkRecordDraw will for us.
81    recorder.scale(2, 2);
82
83    SkRecord rerecord;
84    SkRecorder canvas(&rerecord, W, H);
85    SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL/*bbh*/, NULL/*callback*/);
86
87    int save_count = count_instances_of_type<SkRecords::Save>(rerecord);
88    int restore_count = count_instances_of_type<SkRecords::Save>(rerecord);
89    REPORTER_ASSERT(r, save_count == restore_count);
90}
91
92DEF_TEST(RecordDraw_SetMatrixClobber, r) {
93    // Set up an SkRecord that just scales by 2x,3x.
94    SkRecord scaleRecord;
95    SkRecorder scaleCanvas(&scaleRecord, W, H);
96    SkMatrix scale;
97    scale.setScale(2, 3);
98    scaleCanvas.setMatrix(scale);
99
100    // Set up an SkRecord with an initial +20, +20 translate.
101    SkRecord translateRecord;
102    SkRecorder translateCanvas(&translateRecord, W, H);
103    SkMatrix translate;
104    translate.setTranslate(20, 20);
105    translateCanvas.setMatrix(translate);
106
107    SkRecordDraw(scaleRecord, &translateCanvas, NULL, NULL, 0, NULL/*bbh*/, NULL/*callback*/);
108    REPORTER_ASSERT(r, 4 == translateRecord.count());
109    assert_type<SkRecords::SetMatrix>(r, translateRecord, 0);
110    assert_type<SkRecords::Save>     (r, translateRecord, 1);
111    assert_type<SkRecords::SetMatrix>(r, translateRecord, 2);
112    assert_type<SkRecords::Restore>  (r, translateRecord, 3);
113
114    // When we look at translateRecord now, it should have its first +20,+20 translate,
115    // then a 2x,3x scale that's been concatted with that +20,+20 translate.
116    const SkRecords::SetMatrix* setMatrix;
117    setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 0);
118    REPORTER_ASSERT(r, setMatrix->matrix == translate);
119
120    setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 2);
121    SkMatrix expected = scale;
122    expected.postConcat(translate);
123    REPORTER_ASSERT(r, setMatrix->matrix == expected);
124}
125
126struct TestBBH : public SkBBoxHierarchy {
127    virtual void insert(SkAutoTMalloc<SkRect>* boundsArray, int N) SK_OVERRIDE {
128        fEntries.setCount(N);
129        for (int i = 0; i < N; i++) {
130            Entry e = { (unsigned)i, (*boundsArray)[i] };
131            fEntries[i] = e;
132        }
133    }
134
135    virtual void search(const SkRect& query, SkTDArray<unsigned>* results) const SK_OVERRIDE {}
136    virtual size_t bytesUsed() const SK_OVERRIDE { return 0; }
137
138    struct Entry {
139        unsigned opIndex;
140        SkRect bounds;
141    };
142    SkTDArray<Entry> fEntries;
143};
144
145// Like a==b, with a little slop recognizing that float equality can be weird.
146static bool sloppy_rect_eq(SkRect a, SkRect b) {
147    SkRect inset(a), outset(a);
148    inset.inset(1, 1);
149    outset.outset(1, 1);
150    return outset.contains(b) && !inset.contains(b);
151}
152
153// This test is not meant to make total sense yet.  It's testing the status quo
154// of SkRecordFillBounds(), which itself doesn't make total sense yet.
155DEF_TEST(RecordDraw_BBH, r) {
156    SkRecord record;
157    SkRecorder recorder(&record, W, H);
158    recorder.save();
159        recorder.clipRect(SkRect::MakeWH(400, 500));
160        recorder.scale(2, 2);
161        recorder.drawRect(SkRect::MakeWH(320, 240), SkPaint());
162    recorder.restore();
163
164    TestBBH bbh;
165    SkRecordFillBounds(SkRect::MakeWH(SkIntToScalar(W), SkIntToScalar(H)), record, &bbh);
166
167    REPORTER_ASSERT(r, bbh.fEntries.count() == 5);
168    for (int i = 0; i < bbh.fEntries.count(); i++) {
169        REPORTER_ASSERT(r, bbh.fEntries[i].opIndex == (unsigned)i);
170
171        REPORTER_ASSERT(r, sloppy_rect_eq(SkRect::MakeWH(400, 480), bbh.fEntries[i].bounds));
172    }
173}
174
175// A regression test for crbug.com/409110.
176DEF_TEST(RecordDraw_TextBounds, r) {
177    SkRecord record;
178    SkRecorder recorder(&record, W, H);
179
180    // Two Chinese characters in UTF-8.
181    const char text[] = { '\xe6', '\xbc', '\xa2', '\xe5', '\xad', '\x97' };
182    const size_t bytes = SK_ARRAY_COUNT(text);
183
184    const SkScalar xpos[] = { 10, 20 };
185    recorder.drawPosTextH(text, bytes, xpos, 30, SkPaint());
186
187    const SkPoint pos[] = { {40, 50}, {60, 70} };
188    recorder.drawPosText(text, bytes, pos, SkPaint());
189
190    TestBBH bbh;
191    SkRecordFillBounds(SkRect::MakeWH(SkIntToScalar(W), SkIntToScalar(H)), record, &bbh);
192    REPORTER_ASSERT(r, bbh.fEntries.count() == 2);
193
194    // Font metrics are somewhat platform dependent, so these assertions may need to be adjusted.
195    // But these particular numbers are left over from the days when we used to wildly overestimate
196    // font metrics, so these assertions should actually be pretty safe.
197    REPORTER_ASSERT(r, SkRect::MakeLTRB(0, 0,140, 60).contains(bbh.fEntries[0].bounds));
198    REPORTER_ASSERT(r, SkRect::MakeLTRB(0,20,180,100).contains(bbh.fEntries[1].bounds));
199}
200
201// Base test to ensure start/stop range is respected
202DEF_TEST(RecordDraw_PartialStartStop, r) {
203    static const int kWidth = 10, kHeight = 10;
204
205    SkRect r1 = { 0, 0, kWidth,   kHeight };
206    SkRect r2 = { 0, 0, kWidth,   kHeight/2 };
207    SkRect r3 = { 0, 0, kWidth/2, kHeight };
208    SkPaint p;
209
210    SkRecord record;
211    SkRecorder recorder(&record, kWidth, kHeight);
212    recorder.drawRect(r1, p);
213    recorder.drawRect(r2, p);
214    recorder.drawRect(r3, p);
215
216    SkRecord rerecord;
217    SkRecorder canvas(&rerecord, kWidth, kHeight);
218    SkRecordPartialDraw(record, &canvas, NULL, 0, 1, 2, SkMatrix::I()); // replay just drawRect of r2
219
220    REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord));
221    int index = find_first_instances_of_type<SkRecords::DrawRect>(rerecord);
222    const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, index);
223    REPORTER_ASSERT(r, drawRect->rect == r2);
224}
225
226// A regression test for crbug.com/415468 and skbug.com/2957.
227//
228// This also now serves as a regression test for crbug.com/418417.  We used to adjust the
229// bounds for the saveLayer, clip, and restore to be greater than the bounds of the picture.
230// (We were applying the saveLayer paint to the bounds after restore, which makes no sense.)
231DEF_TEST(RecordDraw_SaveLayerAffectsClipBounds, r) {
232    SkRecord record;
233    SkRecorder recorder(&record, 50, 50);
234
235    // We draw a rectangle with a long drop shadow.  We used to not update the clip
236    // bounds based on SaveLayer paints, so the drop shadow could be cut off.
237    SkPaint paint;
238    paint.setImageFilter(SkDropShadowImageFilter::Create(20, 0, 0, 0, SK_ColorBLACK,
239                         SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode))->unref();
240
241    recorder.saveLayer(NULL, &paint);
242        recorder.clipRect(SkRect::MakeWH(20, 40));
243        recorder.drawRect(SkRect::MakeWH(20, 40), SkPaint());
244    recorder.restore();
245
246    // Under the original bug, the right edge value of the drawRect would be 20 less than asserted
247    // here because we intersected it with a clip that had not been adjusted for the drop shadow.
248    //
249    // The second bug showed up as adjusting the picture bounds (0,0,50,50) by the drop shadow too.
250    // The saveLayer, clipRect, and restore bounds were incorrectly (0,0,70,50).
251    TestBBH bbh;
252    SkRecordFillBounds(SkRect::MakeWH(50, 50), record, &bbh);
253    REPORTER_ASSERT(r, bbh.fEntries.count() == 4);
254    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[0].bounds, SkRect::MakeLTRB(0, 0, 50, 50)));
255    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[1].bounds, SkRect::MakeLTRB(0, 0, 50, 50)));
256    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[2].bounds, SkRect::MakeLTRB(0, 0, 40, 40)));
257    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[3].bounds, SkRect::MakeLTRB(0, 0, 50, 50)));
258}
259
260// When a saveLayer provides an explicit bound and has a complex paint (e.g., one that
261// affects transparent black), that bound should serve to shrink the area of the required
262// backing store.
263DEF_TEST(RecordDraw_SaveLayerBoundsAffectsClipBounds, r) {
264    SkRecord record;
265    SkRecorder recorder(&record, 50, 50);
266
267    SkPaint p;
268    p.setXfermodeMode(SkXfermode::kSrc_Mode);
269
270    SkRect bounds = SkRect::MakeLTRB(10, 10, 40, 40);
271    recorder.saveLayer(&bounds, &p);
272    recorder.drawRect(SkRect::MakeLTRB(20, 20, 30, 30), SkPaint());
273    recorder.restore();
274
275    TestBBH bbh;
276    SkRecordFillBounds(SkRect::MakeWH(50, 50), record, &bbh);
277    REPORTER_ASSERT(r, bbh.fEntries.count() == 3);
278    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[0].bounds, SkRect::MakeLTRB(10, 10, 40, 40)));
279    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[1].bounds, SkRect::MakeLTRB(20, 20, 30, 30)));
280    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[2].bounds, SkRect::MakeLTRB(10, 10, 40, 40)));
281}
282
283DEF_TEST(RecordDraw_drawImage, r){
284    class SkCanvasMock : public SkCanvas {
285    public:
286        SkCanvasMock(int width, int height) : SkCanvas(width, height) {
287            this->resetTestValues();
288        }
289        virtual ~SkCanvasMock() {}
290        virtual void drawImage(const SkImage* image, SkScalar left, SkScalar top,
291                               const SkPaint* paint = NULL) SK_OVERRIDE {
292
293            fDrawImageCalled = true;
294        }
295
296        virtual void drawImageRect(const SkImage* image, const SkRect* src,
297                                   const SkRect& dst,
298                                   const SkPaint* paint = NULL) SK_OVERRIDE {
299            fDrawImageRectCalled = true;
300        }
301
302        void resetTestValues() {
303            fDrawImageCalled = fDrawImageRectCalled = false;
304        }
305
306        bool fDrawImageCalled;
307        bool fDrawImageRectCalled;
308    };
309
310    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(10, 10));
311    surface->getCanvas()->clear(SK_ColorGREEN);
312    SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
313
314    SkCanvasMock canvas(10, 10);
315
316    {
317        SkRecord record;
318        SkRecorder recorder(&record, 10, 10);
319        recorder.drawImage(image, 0, 0);
320        SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL, 0);
321    }
322    REPORTER_ASSERT(r, canvas.fDrawImageCalled);
323    canvas.resetTestValues();
324
325    {
326        SkRecord record;
327        SkRecorder recorder(&record, 10, 10);
328        recorder.drawImageRect(image, 0, SkRect::MakeWH(10, 10));
329        SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL, 0);
330    }
331    REPORTER_ASSERT(r, canvas.fDrawImageRectCalled);
332
333}
334