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 "SkDropShadowImageFilter.h"
13#include "SkImagePriv.h"
14#include "SkRecord.h"
15#include "SkRecordDraw.h"
16#include "SkRecordOpts.h"
17#include "SkRecorder.h"
18#include "SkRecords.h"
19#include "SkSurface.h"
20
21static const int W = 1920, H = 1080;
22
23class JustOneDraw : public SkPicture::AbortCallback {
24public:
25    JustOneDraw() : fCalls(0) {}
26
27    bool abort() override { return fCalls++ > 0; }
28private:
29    int fCalls;
30};
31
32DEF_TEST(RecordDraw_LazySaves, r) {
33    // Record two commands.
34    SkRecord record;
35    SkRecorder recorder(&record, W, H);
36
37    REPORTER_ASSERT(r, 0 == record.count());
38    recorder.save();
39    REPORTER_ASSERT(r, 0 == record.count());    // the save was not recorded (yet)
40    recorder.drawColor(SK_ColorRED);
41    REPORTER_ASSERT(r, 1 == record.count());
42    recorder.scale(2, 2);
43    REPORTER_ASSERT(r, 3 == record.count());    // now we see the save
44    recorder.restore();
45    REPORTER_ASSERT(r, 4 == record.count());
46
47    assert_type<SkRecords::DrawPaint>(r, record, 0);
48    assert_type<SkRecords::Save>     (r, record, 1);
49    assert_type<SkRecords::SetMatrix>(r, record, 2);
50    assert_type<SkRecords::Restore>  (r, record, 3);
51
52    recorder.save();
53    recorder.save();
54    recorder.restore();
55    recorder.restore();
56    REPORTER_ASSERT(r, 4 == record.count());
57}
58
59DEF_TEST(RecordDraw_Abort, r) {
60    // Record two commands.
61    SkRecord record;
62    SkRecorder recorder(&record, W, H);
63    recorder.drawRect(SkRect::MakeWH(200, 300), SkPaint());
64    recorder.clipRect(SkRect::MakeWH(100, 200));
65
66    SkRecord rerecord;
67    SkRecorder canvas(&rerecord, W, H);
68
69    JustOneDraw callback;
70    SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL/*bbh*/, &callback);
71
72    REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord));
73    REPORTER_ASSERT(r, 0 == count_instances_of_type<SkRecords::ClipRect>(rerecord));
74}
75
76DEF_TEST(RecordDraw_Unbalanced, r) {
77    SkRecord record;
78    SkRecorder recorder(&record, W, H);
79    recorder.save();  // We won't balance this, but SkRecordDraw will for us.
80    recorder.scale(2, 2);
81
82    SkRecord rerecord;
83    SkRecorder canvas(&rerecord, W, H);
84    SkRecordDraw(record, &canvas, NULL, NULL, 0, NULL/*bbh*/, NULL/*callback*/);
85
86    int save_count = count_instances_of_type<SkRecords::Save>(rerecord);
87    int restore_count = count_instances_of_type<SkRecords::Save>(rerecord);
88    REPORTER_ASSERT(r, save_count == restore_count);
89}
90
91DEF_TEST(RecordDraw_SetMatrixClobber, r) {
92    // Set up an SkRecord that just scales by 2x,3x.
93    SkRecord scaleRecord;
94    SkRecorder scaleCanvas(&scaleRecord, W, H);
95    SkMatrix scale;
96    scale.setScale(2, 3);
97    scaleCanvas.setMatrix(scale);
98
99    // Set up an SkRecord with an initial +20, +20 translate.
100    SkRecord translateRecord;
101    SkRecorder translateCanvas(&translateRecord, W, H);
102    SkMatrix translate;
103    translate.setTranslate(20, 20);
104    translateCanvas.setMatrix(translate);
105
106    SkRecordDraw(scaleRecord, &translateCanvas, NULL, NULL, 0, NULL/*bbh*/, NULL/*callback*/);
107    REPORTER_ASSERT(r, 4 == translateRecord.count());
108    assert_type<SkRecords::SetMatrix>(r, translateRecord, 0);
109    assert_type<SkRecords::Save>     (r, translateRecord, 1);
110    assert_type<SkRecords::SetMatrix>(r, translateRecord, 2);
111    assert_type<SkRecords::Restore>  (r, translateRecord, 3);
112
113    // When we look at translateRecord now, it should have its first +20,+20 translate,
114    // then a 2x,3x scale that's been concatted with that +20,+20 translate.
115    const SkRecords::SetMatrix* setMatrix;
116    setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 0);
117    REPORTER_ASSERT(r, setMatrix->matrix == translate);
118
119    setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 2);
120    SkMatrix expected = scale;
121    expected.postConcat(translate);
122    REPORTER_ASSERT(r, setMatrix->matrix == expected);
123}
124
125struct TestBBH : public SkBBoxHierarchy {
126    void insert(const SkRect boundsArray[], int N) override {
127        fEntries.setCount(N);
128        for (int i = 0; i < N; i++) {
129            Entry e = { (unsigned)i, boundsArray[i] };
130            fEntries[i] = e;
131        }
132    }
133
134    void search(const SkRect& query, SkTDArray<unsigned>* results) const override {}
135    size_t bytesUsed() const override { return 0; }
136    SkRect getRootBound() const override { return SkRect::MakeEmpty(); }
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    // We can make these next assertions confidently because SkRecordFillBounds
195    // builds its bounds by overestimating font metrics in a platform-independent way.
196    // If that changes, these tests will need to be more flexible.
197    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[0].bounds, SkRect::MakeLTRB(0,  0, 140, 60)));
198    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[1].bounds, SkRect::MakeLTRB(0, 20, 180, 100)));
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    if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
279        REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[0].bounds, SkRect::MakeLTRB(10, 10, 40, 40)));
280        REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[1].bounds, SkRect::MakeLTRB(20, 20, 30, 30)));
281        REPORTER_ASSERT(r, sloppy_rect_eq(bbh.fEntries[2].bounds, SkRect::MakeLTRB(10, 10, 40, 40)));
282    }
283}
284
285DEF_TEST(RecordDraw_drawImage, r){
286    class SkCanvasMock : public SkCanvas {
287    public:
288        SkCanvasMock(int width, int height) : SkCanvas(width, height) {
289            this->resetTestValues();
290        }
291
292        void onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
293                         const SkPaint* paint) override {
294            fDrawImageCalled = true;
295        }
296
297        void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
298                             const SkPaint* paint) 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