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 "SkRecord.h"
12#include "SkRecordOpts.h"
13#include "SkRecorder.h"
14#include "SkRecords.h"
15#include "SkXfermode.h"
16
17static const int W = 1920, H = 1080;
18
19DEF_TEST(RecordOpts_Culling, r) {
20    SkRecord record;
21    SkRecorder recorder(&record, W, H);
22
23    recorder.drawRect(SkRect::MakeWH(1000, 10000), SkPaint());
24
25    recorder.pushCull(SkRect::MakeWH(100, 100));
26        recorder.drawRect(SkRect::MakeWH(10, 10), SkPaint());
27        recorder.drawRect(SkRect::MakeWH(30, 30), SkPaint());
28        recorder.pushCull(SkRect::MakeWH(5, 5));
29            recorder.drawRect(SkRect::MakeWH(1, 1), SkPaint());
30        recorder.popCull();
31    recorder.popCull();
32
33    SkRecordAnnotateCullingPairs(&record);
34
35    REPORTER_ASSERT(r, 6 == assert_type<SkRecords::PairedPushCull>(r, record, 1)->skip);
36    REPORTER_ASSERT(r, 2 == assert_type<SkRecords::PairedPushCull>(r, record, 4)->skip);
37}
38
39DEF_TEST(RecordOpts_NoopCulls, r) {
40    SkRecord record;
41    SkRecorder recorder(&record, W, H);
42
43    // All should be nooped.
44    recorder.pushCull(SkRect::MakeWH(200, 200));
45        recorder.pushCull(SkRect::MakeWH(100, 100));
46        recorder.popCull();
47    recorder.popCull();
48
49    // Kept for now.  We could peel off a layer of culling.
50    recorder.pushCull(SkRect::MakeWH(5, 5));
51        recorder.pushCull(SkRect::MakeWH(5, 5));
52            recorder.drawRect(SkRect::MakeWH(1, 1), SkPaint());
53        recorder.popCull();
54    recorder.popCull();
55
56    SkRecordNoopCulls(&record);
57
58    for (unsigned i = 0; i < 4; i++) {
59        assert_type<SkRecords::NoOp>(r, record, i);
60    }
61    assert_type<SkRecords::PushCull>(r, record, 4);
62    assert_type<SkRecords::PushCull>(r, record, 5);
63    assert_type<SkRecords::DrawRect>(r, record, 6);
64    assert_type<SkRecords::PopCull>(r, record, 7);
65    assert_type<SkRecords::PopCull>(r, record, 8);
66}
67
68static void draw_pos_text(SkCanvas* canvas, const char* text, bool constantY) {
69    const size_t len = strlen(text);
70    SkAutoTMalloc<SkPoint> pos(len);
71    for (size_t i = 0; i < len; i++) {
72        pos[i].fX = (SkScalar)i;
73        pos[i].fY = constantY ? SK_Scalar1 : (SkScalar)i;
74    }
75    canvas->drawPosText(text, len, pos, SkPaint());
76}
77
78DEF_TEST(RecordOpts_StrengthReduction, r) {
79    SkRecord record;
80    SkRecorder recorder(&record, W, H);
81
82    // We can convert a drawPosText into a drawPosTextH when all the Ys are the same.
83    draw_pos_text(&recorder, "This will be reduced to drawPosTextH.", true);
84    draw_pos_text(&recorder, "This cannot be reduced to drawPosTextH.", false);
85
86    SkRecordReduceDrawPosTextStrength(&record);
87
88    assert_type<SkRecords::DrawPosTextH>(r, record, 0);
89    assert_type<SkRecords::DrawPosText>(r, record, 1);
90}
91
92DEF_TEST(RecordOpts_TextBounding, r) {
93    SkRecord record;
94    SkRecorder recorder(&record, W, H);
95
96    // First, get a drawPosTextH.  Here's a handy way.  Its text size will be the default (12).
97    draw_pos_text(&recorder, "This will be reduced to drawPosTextH.", true);
98    SkRecordReduceDrawPosTextStrength(&record);
99
100    const SkRecords::DrawPosTextH* original =
101        assert_type<SkRecords::DrawPosTextH>(r, record, 0);
102
103    // This should wrap the original DrawPosTextH with minY and maxY.
104    SkRecordBoundDrawPosTextH(&record);
105
106    const SkRecords::BoundedDrawPosTextH* bounded =
107        assert_type<SkRecords::BoundedDrawPosTextH>(r, record, 0);
108
109    const SkPaint defaults;
110    REPORTER_ASSERT(r, bounded->base == original);
111    REPORTER_ASSERT(r, bounded->minY <= SK_Scalar1 - defaults.getTextSize());
112    REPORTER_ASSERT(r, bounded->maxY >= SK_Scalar1 + defaults.getTextSize());
113}
114
115DEF_TEST(RecordOpts_NoopDrawSaveRestore, r) {
116    SkRecord record;
117    SkRecorder recorder(&record, W, H);
118
119    // The save and restore are pointless if there's only draw commands in the middle.
120    recorder.save();
121        recorder.drawRect(SkRect::MakeWH(200, 200), SkPaint());
122        recorder.drawRect(SkRect::MakeWH(300, 300), SkPaint());
123        recorder.drawRect(SkRect::MakeWH(100, 100), SkPaint());
124    recorder.restore();
125
126    record.replace<SkRecords::NoOp>(2);  // NoOps should be allowed.
127
128    SkRecordNoopSaveRestores(&record);
129
130    assert_type<SkRecords::NoOp>(r, record, 0);
131    assert_type<SkRecords::DrawRect>(r, record, 1);
132    assert_type<SkRecords::NoOp>(r, record, 2);
133    assert_type<SkRecords::DrawRect>(r, record, 3);
134    assert_type<SkRecords::NoOp>(r, record, 4);
135}
136
137DEF_TEST(RecordOpts_SingleNoopSaveRestore, r) {
138    SkRecord record;
139    SkRecorder recorder(&record, W, H);
140
141    recorder.save();
142        recorder.clipRect(SkRect::MakeWH(200, 200));
143    recorder.restore();
144
145    SkRecordNoopSaveRestores(&record);
146    for (unsigned i = 0; i < 3; i++) {
147        assert_type<SkRecords::NoOp>(r, record, i);
148    }
149}
150
151DEF_TEST(RecordOpts_NoopSaveRestores, r) {
152    SkRecord record;
153    SkRecorder recorder(&record, W, H);
154
155    // The second pass will clean up this pair after the first pass noops all the innards.
156    recorder.save();
157        // A simple pointless pair of save/restore.
158        recorder.save();
159        recorder.restore();
160
161        // As long as we don't draw in there, everything is a noop.
162        recorder.save();
163            recorder.clipRect(SkRect::MakeWH(200, 200));
164            recorder.clipRect(SkRect::MakeWH(100, 100));
165        recorder.restore();
166    recorder.restore();
167
168    SkRecordNoopSaveRestores(&record);
169    for (unsigned index = 0; index < 8; index++) {
170        assert_type<SkRecords::NoOp>(r, record, index);
171    }
172}
173
174static void assert_savelayer_restore(skiatest::Reporter* r,
175                                     SkRecord* record,
176                                     unsigned i,
177                                     bool shouldBeNoOped) {
178    SkRecordNoopSaveLayerDrawRestores(record);
179    if (shouldBeNoOped) {
180        assert_type<SkRecords::NoOp>(r, *record, i);
181        assert_type<SkRecords::NoOp>(r, *record, i+2);
182    } else {
183        assert_type<SkRecords::SaveLayer>(r, *record, i);
184        assert_type<SkRecords::Restore>(r, *record, i+2);
185    }
186}
187
188DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore, r) {
189    SkRecord record;
190    SkRecorder recorder(&record, W, H);
191
192    SkRect bounds = SkRect::MakeWH(100, 200);
193    SkRect   draw = SkRect::MakeWH(50, 60);
194
195    SkPaint goodLayerPaint, badLayerPaint, worseLayerPaint;
196    goodLayerPaint.setColor(0x03000000);  // Only alpha.
197    badLayerPaint.setColor( 0x03040506);  // Not only alpha.
198    worseLayerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);  // Any effect will do.
199
200    SkPaint goodDrawPaint, badDrawPaint;
201    goodDrawPaint.setColor(0xFF020202);  // Opaque.
202    badDrawPaint.setColor( 0x0F020202);  // Not opaque.
203
204    // No change: optimization can't handle bounds.
205    recorder.saveLayer(&bounds, NULL);
206        recorder.drawRect(draw, goodDrawPaint);
207    recorder.restore();
208    assert_savelayer_restore(r, &record, 0, false);
209
210    // SaveLayer/Restore removed: no bounds + no paint = no point.
211    recorder.saveLayer(NULL, NULL);
212        recorder.drawRect(draw, goodDrawPaint);
213    recorder.restore();
214    assert_savelayer_restore(r, &record, 3, true);
215
216    // TODO(mtklein): test case with null draw paint
217
218    // No change: layer paint isn't alpha-only.
219    recorder.saveLayer(NULL, &badLayerPaint);
220        recorder.drawRect(draw, goodDrawPaint);
221    recorder.restore();
222    assert_savelayer_restore(r, &record, 6, false);
223
224    // No change: layer paint has an effect.
225    recorder.saveLayer(NULL, &worseLayerPaint);
226        recorder.drawRect(draw, goodDrawPaint);
227    recorder.restore();
228    assert_savelayer_restore(r, &record, 9, false);
229
230    // No change: draw paint isn't opaque.
231    recorder.saveLayer(NULL, &goodLayerPaint);
232        recorder.drawRect(draw, badDrawPaint);
233    recorder.restore();
234    assert_savelayer_restore(r, &record, 12, false);
235
236    // SaveLayer/Restore removed: we can fold in the alpha!
237    recorder.saveLayer(NULL, &goodLayerPaint);
238        recorder.drawRect(draw, goodDrawPaint);
239    recorder.restore();
240    assert_savelayer_restore(r, &record, 15, true);
241
242    const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, record, 16);
243    REPORTER_ASSERT(r, drawRect != NULL);
244    REPORTER_ASSERT(r, drawRect->paint.getColor() == 0x03020202);
245}
246