1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2012 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBBHFactory.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkClipOp.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFont.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontStyle.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h" // IWYU pragma: keep
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPicture.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPictureRecorder.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixelRef.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSamplingOptions.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypeface.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkBigPicture.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPicturePriv.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRectPriv.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "tools/fonts/FontToolUtils.h"
38*c8dee2aaSAndroid Build Coastguard Worker
39*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
40*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
41*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
42*c8dee2aaSAndroid Build Coastguard Worker
43*c8dee2aaSAndroid Build Coastguard Worker class SkRRect;
44*c8dee2aaSAndroid Build Coastguard Worker class SkRegion;
45*c8dee2aaSAndroid Build Coastguard Worker
make_bm(SkBitmap * bm,int w,int h,SkColor color,bool immutable)46*c8dee2aaSAndroid Build Coastguard Worker static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
47*c8dee2aaSAndroid Build Coastguard Worker bm->allocN32Pixels(w, h);
48*c8dee2aaSAndroid Build Coastguard Worker bm->eraseColor(color);
49*c8dee2aaSAndroid Build Coastguard Worker if (immutable) {
50*c8dee2aaSAndroid Build Coastguard Worker bm->setImmutable();
51*c8dee2aaSAndroid Build Coastguard Worker }
52*c8dee2aaSAndroid Build Coastguard Worker }
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
55*c8dee2aaSAndroid Build Coastguard Worker // Ensure that deleting an empty SkPicture does not assert. Asserts only fire
56*c8dee2aaSAndroid Build Coastguard Worker // in debug mode, so only run in debug mode.
test_deleting_empty_picture()57*c8dee2aaSAndroid Build Coastguard Worker static void test_deleting_empty_picture() {
58*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
59*c8dee2aaSAndroid Build Coastguard Worker // Creates an SkPictureRecord
60*c8dee2aaSAndroid Build Coastguard Worker recorder.beginRecording(0, 0);
61*c8dee2aaSAndroid Build Coastguard Worker // Turns that into an SkPicture
62*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
63*c8dee2aaSAndroid Build Coastguard Worker // Ceates a new SkPictureRecord
64*c8dee2aaSAndroid Build Coastguard Worker recorder.beginRecording(0, 0);
65*c8dee2aaSAndroid Build Coastguard Worker }
66*c8dee2aaSAndroid Build Coastguard Worker
67*c8dee2aaSAndroid Build Coastguard Worker // Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
test_serializing_empty_picture()68*c8dee2aaSAndroid Build Coastguard Worker static void test_serializing_empty_picture() {
69*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
70*c8dee2aaSAndroid Build Coastguard Worker recorder.beginRecording(0, 0);
71*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
72*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream stream;
73*c8dee2aaSAndroid Build Coastguard Worker picture->serialize(&stream, nullptr); // default SkSerialProcs
74*c8dee2aaSAndroid Build Coastguard Worker }
75*c8dee2aaSAndroid Build Coastguard Worker #endif
76*c8dee2aaSAndroid Build Coastguard Worker
rand_op(SkCanvas * canvas,SkRandom & rand)77*c8dee2aaSAndroid Build Coastguard Worker static void rand_op(SkCanvas* canvas, SkRandom& rand) {
78*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
79*c8dee2aaSAndroid Build Coastguard Worker SkRect rect = SkRect::MakeWH(50, 50);
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker SkScalar unit = rand.nextUScalar1();
82*c8dee2aaSAndroid Build Coastguard Worker if (unit <= 0.3) {
83*c8dee2aaSAndroid Build Coastguard Worker // SkDebugf("save\n");
84*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
85*c8dee2aaSAndroid Build Coastguard Worker } else if (unit <= 0.6) {
86*c8dee2aaSAndroid Build Coastguard Worker // SkDebugf("restore\n");
87*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
88*c8dee2aaSAndroid Build Coastguard Worker } else if (unit <= 0.9) {
89*c8dee2aaSAndroid Build Coastguard Worker // SkDebugf("clip\n");
90*c8dee2aaSAndroid Build Coastguard Worker canvas->clipRect(rect);
91*c8dee2aaSAndroid Build Coastguard Worker } else {
92*c8dee2aaSAndroid Build Coastguard Worker // SkDebugf("draw\n");
93*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPaint(paint);
94*c8dee2aaSAndroid Build Coastguard Worker }
95*c8dee2aaSAndroid Build Coastguard Worker }
96*c8dee2aaSAndroid Build Coastguard Worker
set_canvas_to_save_count_4(SkCanvas * canvas)97*c8dee2aaSAndroid Build Coastguard Worker static void set_canvas_to_save_count_4(SkCanvas* canvas) {
98*c8dee2aaSAndroid Build Coastguard Worker canvas->restoreToCount(1);
99*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
100*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
101*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
102*c8dee2aaSAndroid Build Coastguard Worker }
103*c8dee2aaSAndroid Build Coastguard Worker
104*c8dee2aaSAndroid Build Coastguard Worker /**
105*c8dee2aaSAndroid Build Coastguard Worker * A canvas that records the number of saves, saveLayers and restores.
106*c8dee2aaSAndroid Build Coastguard Worker */
107*c8dee2aaSAndroid Build Coastguard Worker class SaveCountingCanvas : public SkCanvas {
108*c8dee2aaSAndroid Build Coastguard Worker public:
SaveCountingCanvas(int width,int height)109*c8dee2aaSAndroid Build Coastguard Worker SaveCountingCanvas(int width, int height)
110*c8dee2aaSAndroid Build Coastguard Worker : INHERITED(width, height)
111*c8dee2aaSAndroid Build Coastguard Worker , fSaveCount(0)
112*c8dee2aaSAndroid Build Coastguard Worker , fSaveLayerCount(0)
113*c8dee2aaSAndroid Build Coastguard Worker , fSaveBehindCount(0)
114*c8dee2aaSAndroid Build Coastguard Worker , fRestoreCount(0){
115*c8dee2aaSAndroid Build Coastguard Worker }
116*c8dee2aaSAndroid Build Coastguard Worker
getSaveLayerStrategy(const SaveLayerRec & rec)117*c8dee2aaSAndroid Build Coastguard Worker SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
118*c8dee2aaSAndroid Build Coastguard Worker ++fSaveLayerCount;
119*c8dee2aaSAndroid Build Coastguard Worker return this->INHERITED::getSaveLayerStrategy(rec);
120*c8dee2aaSAndroid Build Coastguard Worker }
121*c8dee2aaSAndroid Build Coastguard Worker
onDoSaveBehind(const SkRect * subset)122*c8dee2aaSAndroid Build Coastguard Worker bool onDoSaveBehind(const SkRect* subset) override {
123*c8dee2aaSAndroid Build Coastguard Worker ++fSaveBehindCount;
124*c8dee2aaSAndroid Build Coastguard Worker return this->INHERITED::onDoSaveBehind(subset);
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker
willSave()127*c8dee2aaSAndroid Build Coastguard Worker void willSave() override {
128*c8dee2aaSAndroid Build Coastguard Worker ++fSaveCount;
129*c8dee2aaSAndroid Build Coastguard Worker this->INHERITED::willSave();
130*c8dee2aaSAndroid Build Coastguard Worker }
131*c8dee2aaSAndroid Build Coastguard Worker
willRestore()132*c8dee2aaSAndroid Build Coastguard Worker void willRestore() override {
133*c8dee2aaSAndroid Build Coastguard Worker ++fRestoreCount;
134*c8dee2aaSAndroid Build Coastguard Worker this->INHERITED::willRestore();
135*c8dee2aaSAndroid Build Coastguard Worker }
136*c8dee2aaSAndroid Build Coastguard Worker
getSaveCount() const137*c8dee2aaSAndroid Build Coastguard Worker unsigned int getSaveCount() const { return fSaveCount; }
getSaveLayerCount() const138*c8dee2aaSAndroid Build Coastguard Worker unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
getSaveBehindCount() const139*c8dee2aaSAndroid Build Coastguard Worker unsigned int getSaveBehindCount() const { return fSaveBehindCount; }
getRestoreCount() const140*c8dee2aaSAndroid Build Coastguard Worker unsigned int getRestoreCount() const { return fRestoreCount; }
141*c8dee2aaSAndroid Build Coastguard Worker
142*c8dee2aaSAndroid Build Coastguard Worker private:
143*c8dee2aaSAndroid Build Coastguard Worker unsigned int fSaveCount;
144*c8dee2aaSAndroid Build Coastguard Worker unsigned int fSaveLayerCount;
145*c8dee2aaSAndroid Build Coastguard Worker unsigned int fSaveBehindCount;
146*c8dee2aaSAndroid Build Coastguard Worker unsigned int fRestoreCount;
147*c8dee2aaSAndroid Build Coastguard Worker
148*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = SkCanvas;
149*c8dee2aaSAndroid Build Coastguard Worker };
150*c8dee2aaSAndroid Build Coastguard Worker
check_save_state(skiatest::Reporter * reporter,SkPicture * picture,unsigned int numSaves,unsigned int numSaveLayers,unsigned int numRestores)151*c8dee2aaSAndroid Build Coastguard Worker void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
152*c8dee2aaSAndroid Build Coastguard Worker unsigned int numSaves, unsigned int numSaveLayers,
153*c8dee2aaSAndroid Build Coastguard Worker unsigned int numRestores) {
154*c8dee2aaSAndroid Build Coastguard Worker SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
155*c8dee2aaSAndroid Build Coastguard Worker SkScalarCeilToInt(picture->cullRect().height()));
156*c8dee2aaSAndroid Build Coastguard Worker
157*c8dee2aaSAndroid Build Coastguard Worker picture->playback(&canvas);
158*c8dee2aaSAndroid Build Coastguard Worker
159*c8dee2aaSAndroid Build Coastguard Worker // Optimizations may have removed these,
160*c8dee2aaSAndroid Build Coastguard Worker // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
161*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
162*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
163*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker
166*c8dee2aaSAndroid Build Coastguard Worker // This class exists so SkPicture can friend it and give it access to
167*c8dee2aaSAndroid Build Coastguard Worker // the 'partialReplay' method.
168*c8dee2aaSAndroid Build Coastguard Worker class SkPictureRecorderReplayTester {
169*c8dee2aaSAndroid Build Coastguard Worker public:
Copy(SkPictureRecorder * recorder)170*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
171*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder2;
172*c8dee2aaSAndroid Build Coastguard Worker
173*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder2.beginRecording(10, 10);
174*c8dee2aaSAndroid Build Coastguard Worker
175*c8dee2aaSAndroid Build Coastguard Worker recorder->partialReplay(canvas);
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker return recorder2.finishRecordingAsPicture();
178*c8dee2aaSAndroid Build Coastguard Worker }
179*c8dee2aaSAndroid Build Coastguard Worker };
180*c8dee2aaSAndroid Build Coastguard Worker
create_imbalance(SkCanvas * canvas)181*c8dee2aaSAndroid Build Coastguard Worker static void create_imbalance(SkCanvas* canvas) {
182*c8dee2aaSAndroid Build Coastguard Worker SkRect clipRect = SkRect::MakeWH(2, 2);
183*c8dee2aaSAndroid Build Coastguard Worker SkRect drawRect = SkRect::MakeWH(10, 10);
184*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
185*c8dee2aaSAndroid Build Coastguard Worker canvas->clipRect(clipRect, SkClipOp::kIntersect);
186*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(1.0f, 1.0f);
187*c8dee2aaSAndroid Build Coastguard Worker SkPaint p;
188*c8dee2aaSAndroid Build Coastguard Worker p.setColor(SK_ColorGREEN);
189*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(drawRect, p);
190*c8dee2aaSAndroid Build Coastguard Worker // no restore
191*c8dee2aaSAndroid Build Coastguard Worker }
192*c8dee2aaSAndroid Build Coastguard Worker
193*c8dee2aaSAndroid Build Coastguard Worker // This tests that replaying a potentially unbalanced picture into a canvas
194*c8dee2aaSAndroid Build Coastguard Worker // doesn't affect the canvas' save count or matrix/clip state.
check_balance(skiatest::Reporter * reporter,SkPicture * picture)195*c8dee2aaSAndroid Build Coastguard Worker static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
196*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bm;
197*c8dee2aaSAndroid Build Coastguard Worker bm.allocN32Pixels(4, 3);
198*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas(bm);
199*c8dee2aaSAndroid Build Coastguard Worker
200*c8dee2aaSAndroid Build Coastguard Worker int beforeSaveCount = canvas.getSaveCount();
201*c8dee2aaSAndroid Build Coastguard Worker
202*c8dee2aaSAndroid Build Coastguard Worker SkMatrix beforeMatrix = canvas.getTotalMatrix();
203*c8dee2aaSAndroid Build Coastguard Worker
204*c8dee2aaSAndroid Build Coastguard Worker SkRect beforeClip = canvas.getLocalClipBounds();
205*c8dee2aaSAndroid Build Coastguard Worker
206*c8dee2aaSAndroid Build Coastguard Worker canvas.drawPicture(picture);
207*c8dee2aaSAndroid Build Coastguard Worker
208*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
209*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
210*c8dee2aaSAndroid Build Coastguard Worker
211*c8dee2aaSAndroid Build Coastguard Worker SkRect afterClip = canvas.getLocalClipBounds();
212*c8dee2aaSAndroid Build Coastguard Worker
213*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, afterClip == beforeClip);
214*c8dee2aaSAndroid Build Coastguard Worker }
215*c8dee2aaSAndroid Build Coastguard Worker
216*c8dee2aaSAndroid Build Coastguard Worker // Test out SkPictureRecorder::partialReplay
DEF_TEST(PictureRecorder_replay,reporter)217*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(PictureRecorder_replay, reporter) {
218*c8dee2aaSAndroid Build Coastguard Worker // check save/saveLayer state
219*c8dee2aaSAndroid Build Coastguard Worker {
220*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
221*c8dee2aaSAndroid Build Coastguard Worker
222*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(10, 10);
223*c8dee2aaSAndroid Build Coastguard Worker
224*c8dee2aaSAndroid Build Coastguard Worker canvas->saveLayer(nullptr, nullptr);
225*c8dee2aaSAndroid Build Coastguard Worker
226*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
227*c8dee2aaSAndroid Build Coastguard Worker
228*c8dee2aaSAndroid Build Coastguard Worker // The extra save and restore comes from the Copy process.
229*c8dee2aaSAndroid Build Coastguard Worker check_save_state(reporter, copy.get(), 2, 1, 3);
230*c8dee2aaSAndroid Build Coastguard Worker
231*c8dee2aaSAndroid Build Coastguard Worker canvas->saveLayer(nullptr, nullptr);
232*c8dee2aaSAndroid Build Coastguard Worker
233*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
234*c8dee2aaSAndroid Build Coastguard Worker
235*c8dee2aaSAndroid Build Coastguard Worker check_save_state(reporter, final.get(), 1, 2, 3);
236*c8dee2aaSAndroid Build Coastguard Worker
237*c8dee2aaSAndroid Build Coastguard Worker // The copy shouldn't pick up any operations added after it was made
238*c8dee2aaSAndroid Build Coastguard Worker check_save_state(reporter, copy.get(), 2, 1, 3);
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker
241*c8dee2aaSAndroid Build Coastguard Worker // Recreate the Android partialReplay test case
242*c8dee2aaSAndroid Build Coastguard Worker {
243*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
244*c8dee2aaSAndroid Build Coastguard Worker
245*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(4, 3);
246*c8dee2aaSAndroid Build Coastguard Worker create_imbalance(canvas);
247*c8dee2aaSAndroid Build Coastguard Worker
248*c8dee2aaSAndroid Build Coastguard Worker int expectedSaveCount = canvas->getSaveCount();
249*c8dee2aaSAndroid Build Coastguard Worker
250*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
251*c8dee2aaSAndroid Build Coastguard Worker check_balance(reporter, copy.get());
252*c8dee2aaSAndroid Build Coastguard Worker
253*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
254*c8dee2aaSAndroid Build Coastguard Worker
255*c8dee2aaSAndroid Build Coastguard Worker // End the recording of source to test the picture finalization
256*c8dee2aaSAndroid Build Coastguard Worker // process isn't complicated by the partialReplay step
257*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
258*c8dee2aaSAndroid Build Coastguard Worker }
259*c8dee2aaSAndroid Build Coastguard Worker }
260*c8dee2aaSAndroid Build Coastguard Worker
test_unbalanced_save_restores(skiatest::Reporter * reporter)261*c8dee2aaSAndroid Build Coastguard Worker static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
262*c8dee2aaSAndroid Build Coastguard Worker SkCanvas testCanvas(100, 100);
263*c8dee2aaSAndroid Build Coastguard Worker set_canvas_to_save_count_4(&testCanvas);
264*c8dee2aaSAndroid Build Coastguard Worker
265*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
266*c8dee2aaSAndroid Build Coastguard Worker
267*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
268*c8dee2aaSAndroid Build Coastguard Worker SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
269*c8dee2aaSAndroid Build Coastguard Worker
270*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
271*c8dee2aaSAndroid Build Coastguard Worker
272*c8dee2aaSAndroid Build Coastguard Worker {
273*c8dee2aaSAndroid Build Coastguard Worker // Create picture with 2 unbalanced saves
274*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(100, 100);
275*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
276*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(10, 10);
277*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(rect, paint);
278*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
279*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(10, 10);
280*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(rect, paint);
281*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
282*c8dee2aaSAndroid Build Coastguard Worker
283*c8dee2aaSAndroid Build Coastguard Worker testCanvas.drawPicture(extraSavePicture);
284*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
285*c8dee2aaSAndroid Build Coastguard Worker }
286*c8dee2aaSAndroid Build Coastguard Worker
287*c8dee2aaSAndroid Build Coastguard Worker set_canvas_to_save_count_4(&testCanvas);
288*c8dee2aaSAndroid Build Coastguard Worker
289*c8dee2aaSAndroid Build Coastguard Worker {
290*c8dee2aaSAndroid Build Coastguard Worker // Create picture with 2 unbalanced restores
291*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(100, 100);
292*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
293*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(10, 10);
294*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(rect, paint);
295*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
296*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(10, 10);
297*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(rect, paint);
298*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
299*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
300*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
301*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
302*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
303*c8dee2aaSAndroid Build Coastguard Worker
304*c8dee2aaSAndroid Build Coastguard Worker testCanvas.drawPicture(extraRestorePicture);
305*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
306*c8dee2aaSAndroid Build Coastguard Worker }
307*c8dee2aaSAndroid Build Coastguard Worker
308*c8dee2aaSAndroid Build Coastguard Worker set_canvas_to_save_count_4(&testCanvas);
309*c8dee2aaSAndroid Build Coastguard Worker
310*c8dee2aaSAndroid Build Coastguard Worker {
311*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(100, 100);
312*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(10, 10);
313*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(rect, paint);
314*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
315*c8dee2aaSAndroid Build Coastguard Worker
316*c8dee2aaSAndroid Build Coastguard Worker testCanvas.drawPicture(noSavePicture);
317*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
318*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
319*c8dee2aaSAndroid Build Coastguard Worker }
320*c8dee2aaSAndroid Build Coastguard Worker }
321*c8dee2aaSAndroid Build Coastguard Worker
test_peephole()322*c8dee2aaSAndroid Build Coastguard Worker static void test_peephole() {
323*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand;
324*c8dee2aaSAndroid Build Coastguard Worker
325*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
326*c8dee2aaSAndroid Build Coastguard Worker
327*c8dee2aaSAndroid Build Coastguard Worker for (int j = 0; j < 100; j++) {
328*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand2(rand); // remember the seed
329*c8dee2aaSAndroid Build Coastguard Worker
330*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(100, 100);
331*c8dee2aaSAndroid Build Coastguard Worker
332*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 1000; ++i) {
333*c8dee2aaSAndroid Build Coastguard Worker rand_op(canvas, rand);
334*c8dee2aaSAndroid Build Coastguard Worker }
335*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
336*c8dee2aaSAndroid Build Coastguard Worker
337*c8dee2aaSAndroid Build Coastguard Worker rand = rand2;
338*c8dee2aaSAndroid Build Coastguard Worker }
339*c8dee2aaSAndroid Build Coastguard Worker
340*c8dee2aaSAndroid Build Coastguard Worker {
341*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(100, 100);
342*c8dee2aaSAndroid Build Coastguard Worker SkRect rect = SkRect::MakeWH(50, 50);
343*c8dee2aaSAndroid Build Coastguard Worker
344*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 100; ++i) {
345*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
346*c8dee2aaSAndroid Build Coastguard Worker }
347*c8dee2aaSAndroid Build Coastguard Worker while (canvas->getSaveCount() > 1) {
348*c8dee2aaSAndroid Build Coastguard Worker canvas->clipRect(rect);
349*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
350*c8dee2aaSAndroid Build Coastguard Worker }
351*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
352*c8dee2aaSAndroid Build Coastguard Worker }
353*c8dee2aaSAndroid Build Coastguard Worker }
354*c8dee2aaSAndroid Build Coastguard Worker
test_bad_bitmap(skiatest::Reporter * reporter)355*c8dee2aaSAndroid Build Coastguard Worker static void test_bad_bitmap(skiatest::Reporter* reporter) {
356*c8dee2aaSAndroid Build Coastguard Worker // missing pixels should return null for image
357*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bm;
358*c8dee2aaSAndroid Build Coastguard Worker bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
359*c8dee2aaSAndroid Build Coastguard Worker auto img = bm.asImage();
360*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !img);
361*c8dee2aaSAndroid Build Coastguard Worker
362*c8dee2aaSAndroid Build Coastguard Worker // make sure we don't crash on a null image
363*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
364*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
365*c8dee2aaSAndroid Build Coastguard Worker recordingCanvas->drawImage(nullptr, 0, 0);
366*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
367*c8dee2aaSAndroid Build Coastguard Worker
368*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas;
369*c8dee2aaSAndroid Build Coastguard Worker canvas.drawPicture(picture);
370*c8dee2aaSAndroid Build Coastguard Worker }
371*c8dee2aaSAndroid Build Coastguard Worker
test_clip_bound_opt(skiatest::Reporter * reporter)372*c8dee2aaSAndroid Build Coastguard Worker static void test_clip_bound_opt(skiatest::Reporter* reporter) {
373*c8dee2aaSAndroid Build Coastguard Worker // Test for crbug.com/229011
374*c8dee2aaSAndroid Build Coastguard Worker SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
375*c8dee2aaSAndroid Build Coastguard Worker SkIntToScalar(2), SkIntToScalar(2));
376*c8dee2aaSAndroid Build Coastguard Worker SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
377*c8dee2aaSAndroid Build Coastguard Worker SkIntToScalar(1), SkIntToScalar(1));
378*c8dee2aaSAndroid Build Coastguard Worker SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
379*c8dee2aaSAndroid Build Coastguard Worker SkIntToScalar(1), SkIntToScalar(1));
380*c8dee2aaSAndroid Build Coastguard Worker
381*c8dee2aaSAndroid Build Coastguard Worker SkPath invPath;
382*c8dee2aaSAndroid Build Coastguard Worker invPath.addOval(rect1);
383*c8dee2aaSAndroid Build Coastguard Worker invPath.setFillType(SkPathFillType::kInverseEvenOdd);
384*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
385*c8dee2aaSAndroid Build Coastguard Worker path.addOval(rect2);
386*c8dee2aaSAndroid Build Coastguard Worker SkPath path2;
387*c8dee2aaSAndroid Build Coastguard Worker path2.addOval(rect3);
388*c8dee2aaSAndroid Build Coastguard Worker SkIRect clipBounds;
389*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
390*c8dee2aaSAndroid Build Coastguard Worker
391*c8dee2aaSAndroid Build Coastguard Worker // Testing conservative-raster-clip that is enabled by PictureRecord
392*c8dee2aaSAndroid Build Coastguard Worker {
393*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(10, 10);
394*c8dee2aaSAndroid Build Coastguard Worker canvas->clipPath(invPath);
395*c8dee2aaSAndroid Build Coastguard Worker clipBounds = canvas->getDeviceClipBounds();
396*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
397*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
398*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
399*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
400*c8dee2aaSAndroid Build Coastguard Worker }
401*c8dee2aaSAndroid Build Coastguard Worker {
402*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(10, 10);
403*c8dee2aaSAndroid Build Coastguard Worker canvas->clipPath(path);
404*c8dee2aaSAndroid Build Coastguard Worker canvas->clipPath(invPath);
405*c8dee2aaSAndroid Build Coastguard Worker clipBounds = canvas->getDeviceClipBounds();
406*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
407*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
408*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
409*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
410*c8dee2aaSAndroid Build Coastguard Worker }
411*c8dee2aaSAndroid Build Coastguard Worker {
412*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(10, 10);
413*c8dee2aaSAndroid Build Coastguard Worker canvas->clipPath(path, SkClipOp::kDifference);
414*c8dee2aaSAndroid Build Coastguard Worker clipBounds = canvas->getDeviceClipBounds();
415*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
416*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
417*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
418*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
419*c8dee2aaSAndroid Build Coastguard Worker }
420*c8dee2aaSAndroid Build Coastguard Worker {
421*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(10, 10);
422*c8dee2aaSAndroid Build Coastguard Worker canvas->clipPath(path, SkClipOp::kIntersect);
423*c8dee2aaSAndroid Build Coastguard Worker canvas->clipPath(path2, SkClipOp::kDifference);
424*c8dee2aaSAndroid Build Coastguard Worker clipBounds = canvas->getDeviceClipBounds();
425*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
426*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
427*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
428*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
429*c8dee2aaSAndroid Build Coastguard Worker }
430*c8dee2aaSAndroid Build Coastguard Worker }
431*c8dee2aaSAndroid Build Coastguard Worker
test_cull_rect_reset(skiatest::Reporter * reporter)432*c8dee2aaSAndroid Build Coastguard Worker static void test_cull_rect_reset(skiatest::Reporter* reporter) {
433*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
434*c8dee2aaSAndroid Build Coastguard Worker SkRect bounds = SkRect::MakeWH(10, 10);
435*c8dee2aaSAndroid Build Coastguard Worker SkRTreeFactory factory;
436*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
437*c8dee2aaSAndroid Build Coastguard Worker bounds = SkRect::MakeWH(100, 100);
438*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
439*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(bounds, paint);
440*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(bounds, paint);
441*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
442*c8dee2aaSAndroid Build Coastguard Worker const SkBigPicture* picture = SkPicturePriv::AsSkBigPicture(p);
443*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, picture);
444*c8dee2aaSAndroid Build Coastguard Worker
445*c8dee2aaSAndroid Build Coastguard Worker SkRect finalCullRect = picture->cullRect();
446*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
447*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
448*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
449*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
450*c8dee2aaSAndroid Build Coastguard Worker }
451*c8dee2aaSAndroid Build Coastguard Worker
452*c8dee2aaSAndroid Build Coastguard Worker
453*c8dee2aaSAndroid Build Coastguard Worker /**
454*c8dee2aaSAndroid Build Coastguard Worker * A canvas that records the number of clip commands.
455*c8dee2aaSAndroid Build Coastguard Worker */
456*c8dee2aaSAndroid Build Coastguard Worker class ClipCountingCanvas : public SkCanvas {
457*c8dee2aaSAndroid Build Coastguard Worker public:
ClipCountingCanvas(int width,int height)458*c8dee2aaSAndroid Build Coastguard Worker ClipCountingCanvas(int width, int height)
459*c8dee2aaSAndroid Build Coastguard Worker : INHERITED(width, height)
460*c8dee2aaSAndroid Build Coastguard Worker , fClipCount(0){
461*c8dee2aaSAndroid Build Coastguard Worker }
462*c8dee2aaSAndroid Build Coastguard Worker
onClipRect(const SkRect & r,SkClipOp op,ClipEdgeStyle edgeStyle)463*c8dee2aaSAndroid Build Coastguard Worker void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
464*c8dee2aaSAndroid Build Coastguard Worker fClipCount += 1;
465*c8dee2aaSAndroid Build Coastguard Worker this->INHERITED::onClipRect(r, op, edgeStyle);
466*c8dee2aaSAndroid Build Coastguard Worker }
467*c8dee2aaSAndroid Build Coastguard Worker
onClipRRect(const SkRRect & rrect,SkClipOp op,ClipEdgeStyle edgeStyle)468*c8dee2aaSAndroid Build Coastguard Worker void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
469*c8dee2aaSAndroid Build Coastguard Worker fClipCount += 1;
470*c8dee2aaSAndroid Build Coastguard Worker this->INHERITED::onClipRRect(rrect, op, edgeStyle);
471*c8dee2aaSAndroid Build Coastguard Worker }
472*c8dee2aaSAndroid Build Coastguard Worker
onClipPath(const SkPath & path,SkClipOp op,ClipEdgeStyle edgeStyle)473*c8dee2aaSAndroid Build Coastguard Worker void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
474*c8dee2aaSAndroid Build Coastguard Worker fClipCount += 1;
475*c8dee2aaSAndroid Build Coastguard Worker this->INHERITED::onClipPath(path, op, edgeStyle);
476*c8dee2aaSAndroid Build Coastguard Worker }
477*c8dee2aaSAndroid Build Coastguard Worker
onClipRegion(const SkRegion & deviceRgn,SkClipOp op)478*c8dee2aaSAndroid Build Coastguard Worker void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
479*c8dee2aaSAndroid Build Coastguard Worker fClipCount += 1;
480*c8dee2aaSAndroid Build Coastguard Worker this->INHERITED::onClipRegion(deviceRgn, op);
481*c8dee2aaSAndroid Build Coastguard Worker }
482*c8dee2aaSAndroid Build Coastguard Worker
getClipCount() const483*c8dee2aaSAndroid Build Coastguard Worker unsigned getClipCount() const { return fClipCount; }
484*c8dee2aaSAndroid Build Coastguard Worker
485*c8dee2aaSAndroid Build Coastguard Worker private:
486*c8dee2aaSAndroid Build Coastguard Worker unsigned fClipCount;
487*c8dee2aaSAndroid Build Coastguard Worker
488*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = SkCanvas;
489*c8dee2aaSAndroid Build Coastguard Worker };
490*c8dee2aaSAndroid Build Coastguard Worker
test_gen_id(skiatest::Reporter * reporter)491*c8dee2aaSAndroid Build Coastguard Worker static void test_gen_id(skiatest::Reporter* reporter) {
492*c8dee2aaSAndroid Build Coastguard Worker
493*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
494*c8dee2aaSAndroid Build Coastguard Worker recorder.beginRecording(0, 0);
495*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
496*c8dee2aaSAndroid Build Coastguard Worker
497*c8dee2aaSAndroid Build Coastguard Worker // Empty pictures should still have a valid ID
498*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
499*c8dee2aaSAndroid Build Coastguard Worker
500*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(1, 1);
501*c8dee2aaSAndroid Build Coastguard Worker canvas->drawColor(SK_ColorWHITE);
502*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
503*c8dee2aaSAndroid Build Coastguard Worker // picture should have a non-zero id after recording
504*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
505*c8dee2aaSAndroid Build Coastguard Worker
506*c8dee2aaSAndroid Build Coastguard Worker // both pictures should have different ids
507*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
508*c8dee2aaSAndroid Build Coastguard Worker }
509*c8dee2aaSAndroid Build Coastguard Worker
test_typeface(skiatest::Reporter * reporter)510*c8dee2aaSAndroid Build Coastguard Worker static void test_typeface(skiatest::Reporter* reporter) {
511*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
512*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(10, 10);
513*c8dee2aaSAndroid Build Coastguard Worker SkFont font(ToolUtils::CreateTestTypeface("Arial", SkFontStyle::Italic()));
514*c8dee2aaSAndroid Build Coastguard Worker canvas->drawString("Q", 0, 10, font, SkPaint());
515*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
516*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream stream;
517*c8dee2aaSAndroid Build Coastguard Worker picture->serialize(&stream, nullptr); // default SkSerialProcs
518*c8dee2aaSAndroid Build Coastguard Worker }
519*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Picture,reporter)520*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture, reporter) {
521*c8dee2aaSAndroid Build Coastguard Worker test_typeface(reporter);
522*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
523*c8dee2aaSAndroid Build Coastguard Worker test_deleting_empty_picture();
524*c8dee2aaSAndroid Build Coastguard Worker test_serializing_empty_picture();
525*c8dee2aaSAndroid Build Coastguard Worker #endif
526*c8dee2aaSAndroid Build Coastguard Worker test_bad_bitmap(reporter);
527*c8dee2aaSAndroid Build Coastguard Worker test_unbalanced_save_restores(reporter);
528*c8dee2aaSAndroid Build Coastguard Worker test_peephole();
529*c8dee2aaSAndroid Build Coastguard Worker test_clip_bound_opt(reporter);
530*c8dee2aaSAndroid Build Coastguard Worker test_gen_id(reporter);
531*c8dee2aaSAndroid Build Coastguard Worker test_cull_rect_reset(reporter);
532*c8dee2aaSAndroid Build Coastguard Worker }
533*c8dee2aaSAndroid Build Coastguard Worker
draw_bitmaps(const SkBitmap & bitmap,SkCanvas * canvas)534*c8dee2aaSAndroid Build Coastguard Worker static void draw_bitmaps(const SkBitmap& bitmap, SkCanvas* canvas) {
535*c8dee2aaSAndroid Build Coastguard Worker const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
536*c8dee2aaSAndroid Build Coastguard Worker auto img = bitmap.asImage();
537*c8dee2aaSAndroid Build Coastguard Worker
538*c8dee2aaSAndroid Build Coastguard Worker // Don't care what these record, as long as they're legal.
539*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(img, 0.0f, 0.0f);
540*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImageRect(img, rect, rect, SkSamplingOptions(), nullptr,
541*c8dee2aaSAndroid Build Coastguard Worker SkCanvas::kStrict_SrcRectConstraint);
542*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(img, 1, 1); // drawSprite
543*c8dee2aaSAndroid Build Coastguard Worker }
544*c8dee2aaSAndroid Build Coastguard Worker
test_draw_bitmaps(SkCanvas * canvas)545*c8dee2aaSAndroid Build Coastguard Worker static void test_draw_bitmaps(SkCanvas* canvas) {
546*c8dee2aaSAndroid Build Coastguard Worker SkBitmap empty;
547*c8dee2aaSAndroid Build Coastguard Worker draw_bitmaps(empty, canvas);
548*c8dee2aaSAndroid Build Coastguard Worker empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
549*c8dee2aaSAndroid Build Coastguard Worker draw_bitmaps(empty, canvas);
550*c8dee2aaSAndroid Build Coastguard Worker }
551*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Picture_EmptyBitmap,r)552*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_EmptyBitmap, r) {
553*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
554*c8dee2aaSAndroid Build Coastguard Worker test_draw_bitmaps(recorder.beginRecording(10, 10));
555*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
556*c8dee2aaSAndroid Build Coastguard Worker }
557*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Canvas_EmptyBitmap,r)558*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Canvas_EmptyBitmap, r) {
559*c8dee2aaSAndroid Build Coastguard Worker SkBitmap dst;
560*c8dee2aaSAndroid Build Coastguard Worker dst.allocN32Pixels(10, 10);
561*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas(dst);
562*c8dee2aaSAndroid Build Coastguard Worker
563*c8dee2aaSAndroid Build Coastguard Worker test_draw_bitmaps(&canvas);
564*c8dee2aaSAndroid Build Coastguard Worker }
565*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore,reporter)566*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
567*c8dee2aaSAndroid Build Coastguard Worker // This test is from crbug.com/344987.
568*c8dee2aaSAndroid Build Coastguard Worker // The commands are:
569*c8dee2aaSAndroid Build Coastguard Worker // saveLayer with paint that modifies alpha
570*c8dee2aaSAndroid Build Coastguard Worker // drawBitmapRect
571*c8dee2aaSAndroid Build Coastguard Worker // drawBitmapRect
572*c8dee2aaSAndroid Build Coastguard Worker // restore
573*c8dee2aaSAndroid Build Coastguard Worker // The bug was that this structure was modified so that:
574*c8dee2aaSAndroid Build Coastguard Worker // - The saveLayer and restore were eliminated
575*c8dee2aaSAndroid Build Coastguard Worker // - The alpha was only applied to the first drawBitmapRectToRect
576*c8dee2aaSAndroid Build Coastguard Worker
577*c8dee2aaSAndroid Build Coastguard Worker // This test draws blue and red squares inside a 50% transparent
578*c8dee2aaSAndroid Build Coastguard Worker // layer. Both colours should show up muted.
579*c8dee2aaSAndroid Build Coastguard Worker // When the bug is present, the red square (the second bitmap)
580*c8dee2aaSAndroid Build Coastguard Worker // shows upwith full opacity.
581*c8dee2aaSAndroid Build Coastguard Worker
582*c8dee2aaSAndroid Build Coastguard Worker SkBitmap blueBM;
583*c8dee2aaSAndroid Build Coastguard Worker make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
584*c8dee2aaSAndroid Build Coastguard Worker SkBitmap redBM;
585*c8dee2aaSAndroid Build Coastguard Worker make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
586*c8dee2aaSAndroid Build Coastguard Worker SkPaint semiTransparent;
587*c8dee2aaSAndroid Build Coastguard Worker semiTransparent.setAlpha(0x80);
588*c8dee2aaSAndroid Build Coastguard Worker
589*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
590*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(100, 100);
591*c8dee2aaSAndroid Build Coastguard Worker canvas->drawColor(0);
592*c8dee2aaSAndroid Build Coastguard Worker
593*c8dee2aaSAndroid Build Coastguard Worker canvas->saveLayer(nullptr, &semiTransparent);
594*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(blueBM.asImage(), 25, 25);
595*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(redBM.asImage(), 50, 50);
596*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
597*c8dee2aaSAndroid Build Coastguard Worker
598*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
599*c8dee2aaSAndroid Build Coastguard Worker
600*c8dee2aaSAndroid Build Coastguard Worker // Now replay the picture back on another canvas
601*c8dee2aaSAndroid Build Coastguard Worker // and check a couple of its pixels.
602*c8dee2aaSAndroid Build Coastguard Worker SkBitmap replayBM;
603*c8dee2aaSAndroid Build Coastguard Worker make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
604*c8dee2aaSAndroid Build Coastguard Worker SkCanvas replayCanvas(replayBM);
605*c8dee2aaSAndroid Build Coastguard Worker picture->playback(&replayCanvas);
606*c8dee2aaSAndroid Build Coastguard Worker
607*c8dee2aaSAndroid Build Coastguard Worker // With the bug present, at (55, 55) we would get a fully opaque red
608*c8dee2aaSAndroid Build Coastguard Worker // intead of a dark red.
609*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
610*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
611*c8dee2aaSAndroid Build Coastguard Worker }
612*c8dee2aaSAndroid Build Coastguard Worker
613*c8dee2aaSAndroid Build Coastguard Worker struct CountingBBH : public SkBBoxHierarchy {
614*c8dee2aaSAndroid Build Coastguard Worker mutable int searchCalls;
615*c8dee2aaSAndroid Build Coastguard Worker
CountingBBHCountingBBH616*c8dee2aaSAndroid Build Coastguard Worker CountingBBH() : searchCalls(0) {}
617*c8dee2aaSAndroid Build Coastguard Worker
searchCountingBBH618*c8dee2aaSAndroid Build Coastguard Worker void search(const SkRect& query, std::vector<int>* results) const override {
619*c8dee2aaSAndroid Build Coastguard Worker this->searchCalls++;
620*c8dee2aaSAndroid Build Coastguard Worker }
621*c8dee2aaSAndroid Build Coastguard Worker
insertCountingBBH622*c8dee2aaSAndroid Build Coastguard Worker void insert(const SkRect[], int) override {}
bytesUsedCountingBBH623*c8dee2aaSAndroid Build Coastguard Worker size_t bytesUsed() const override { return 0; }
624*c8dee2aaSAndroid Build Coastguard Worker };
625*c8dee2aaSAndroid Build Coastguard Worker
626*c8dee2aaSAndroid Build Coastguard Worker class SpoonFedBBHFactory : public SkBBHFactory {
627*c8dee2aaSAndroid Build Coastguard Worker public:
SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh)628*c8dee2aaSAndroid Build Coastguard Worker explicit SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh) : fBBH(std::move(bbh)) {}
operator ()() const629*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkBBoxHierarchy> operator()() const override {
630*c8dee2aaSAndroid Build Coastguard Worker return fBBH;
631*c8dee2aaSAndroid Build Coastguard Worker }
632*c8dee2aaSAndroid Build Coastguard Worker private:
633*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkBBoxHierarchy> fBBH;
634*c8dee2aaSAndroid Build Coastguard Worker };
635*c8dee2aaSAndroid Build Coastguard Worker
636*c8dee2aaSAndroid Build Coastguard Worker // When the canvas clip covers the full picture, we don't need to call the BBH.
DEF_TEST(Picture_SkipBBH,r)637*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_SkipBBH, r) {
638*c8dee2aaSAndroid Build Coastguard Worker SkRect bound = SkRect::MakeWH(320, 240);
639*c8dee2aaSAndroid Build Coastguard Worker
640*c8dee2aaSAndroid Build Coastguard Worker auto bbh = sk_make_sp<CountingBBH>();
641*c8dee2aaSAndroid Build Coastguard Worker SpoonFedBBHFactory factory(bbh);
642*c8dee2aaSAndroid Build Coastguard Worker
643*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
644*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* c = recorder.beginRecording(bound, &factory);
645*c8dee2aaSAndroid Build Coastguard Worker // Record a few ops so we don't hit a small- or empty- picture optimization.
646*c8dee2aaSAndroid Build Coastguard Worker c->drawRect(bound, SkPaint());
647*c8dee2aaSAndroid Build Coastguard Worker c->drawRect(bound, SkPaint());
648*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
649*c8dee2aaSAndroid Build Coastguard Worker
650*c8dee2aaSAndroid Build Coastguard Worker SkCanvas big(640, 480), small(300, 200);
651*c8dee2aaSAndroid Build Coastguard Worker
652*c8dee2aaSAndroid Build Coastguard Worker picture->playback(&big);
653*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, bbh->searchCalls == 0);
654*c8dee2aaSAndroid Build Coastguard Worker
655*c8dee2aaSAndroid Build Coastguard Worker picture->playback(&small);
656*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, bbh->searchCalls == 1);
657*c8dee2aaSAndroid Build Coastguard Worker }
658*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Picture_BitmapLeak,r)659*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_BitmapLeak, r) {
660*c8dee2aaSAndroid Build Coastguard Worker SkBitmap mut, immut;
661*c8dee2aaSAndroid Build Coastguard Worker mut.allocN32Pixels(300, 200);
662*c8dee2aaSAndroid Build Coastguard Worker immut.allocN32Pixels(300, 200);
663*c8dee2aaSAndroid Build Coastguard Worker immut.setImmutable();
664*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!mut.isImmutable());
665*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(immut.isImmutable());
666*c8dee2aaSAndroid Build Coastguard Worker
667*c8dee2aaSAndroid Build Coastguard Worker // No one can hold a ref on our pixels yet.
668*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, mut.pixelRef()->unique());
669*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, immut.pixelRef()->unique());
670*c8dee2aaSAndroid Build Coastguard Worker
671*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> pic;
672*c8dee2aaSAndroid Build Coastguard Worker {
673*c8dee2aaSAndroid Build Coastguard Worker // we want the recorder to go out of scope before our subsequent checks, so we
674*c8dee2aaSAndroid Build Coastguard Worker // place it inside local braces.
675*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder rec;
676*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = rec.beginRecording(1920, 1200);
677*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(mut.asImage(), 0, 0);
678*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(immut.asImage(), 800, 600);
679*c8dee2aaSAndroid Build Coastguard Worker pic = rec.finishRecordingAsPicture();
680*c8dee2aaSAndroid Build Coastguard Worker }
681*c8dee2aaSAndroid Build Coastguard Worker
682*c8dee2aaSAndroid Build Coastguard Worker // The picture shares the immutable pixels but copies the mutable ones.
683*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, mut.pixelRef()->unique());
684*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !immut.pixelRef()->unique());
685*c8dee2aaSAndroid Build Coastguard Worker
686*c8dee2aaSAndroid Build Coastguard Worker // When the picture goes away, it's just our bitmaps holding the refs.
687*c8dee2aaSAndroid Build Coastguard Worker pic = nullptr;
688*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, mut.pixelRef()->unique());
689*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, immut.pixelRef()->unique());
690*c8dee2aaSAndroid Build Coastguard Worker }
691*c8dee2aaSAndroid Build Coastguard Worker
692*c8dee2aaSAndroid Build Coastguard Worker // getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
DEF_TEST(Picture_getRecordingCanvas,r)693*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_getRecordingCanvas, r) {
694*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder rec;
695*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !rec.getRecordingCanvas());
696*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 3; i++) {
697*c8dee2aaSAndroid Build Coastguard Worker rec.beginRecording(100, 100);
698*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, rec.getRecordingCanvas());
699*c8dee2aaSAndroid Build Coastguard Worker rec.finishRecordingAsPicture();
700*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !rec.getRecordingCanvas());
701*c8dee2aaSAndroid Build Coastguard Worker }
702*c8dee2aaSAndroid Build Coastguard Worker }
703*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Picture_preserveCullRect,r)704*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_preserveCullRect, r) {
705*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
706*c8dee2aaSAndroid Build Coastguard Worker
707*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
708*c8dee2aaSAndroid Build Coastguard Worker c->clear(SK_ColorCYAN);
709*c8dee2aaSAndroid Build Coastguard Worker
710*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
711*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream wstream;
712*c8dee2aaSAndroid Build Coastguard Worker picture->serialize(&wstream, nullptr); // default SkSerialProcs
713*c8dee2aaSAndroid Build Coastguard Worker
714*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
715*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
716*c8dee2aaSAndroid Build Coastguard Worker
717*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, deserializedPicture != nullptr);
718*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
719*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
720*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
721*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
722*c8dee2aaSAndroid Build Coastguard Worker }
723*c8dee2aaSAndroid Build Coastguard Worker
724*c8dee2aaSAndroid Build Coastguard Worker
725*c8dee2aaSAndroid Build Coastguard Worker // If we record bounded ops into a picture with a big cull and calculate the
726*c8dee2aaSAndroid Build Coastguard Worker // bounds of those ops, we should trim down the picture cull to the ops' bounds.
727*c8dee2aaSAndroid Build Coastguard Worker // If we're not using an SkBBH, we shouldn't change it.
DEF_TEST(Picture_UpdatedCull_1,r)728*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_UpdatedCull_1, r) {
729*c8dee2aaSAndroid Build Coastguard Worker SkRTreeFactory factory;
730*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
731*c8dee2aaSAndroid Build Coastguard Worker
732*c8dee2aaSAndroid Build Coastguard Worker auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
733*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
734*c8dee2aaSAndroid Build Coastguard Worker auto pic = recorder.finishRecordingAsPicture();
735*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
736*c8dee2aaSAndroid Build Coastguard Worker
737*c8dee2aaSAndroid Build Coastguard Worker canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
738*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
739*c8dee2aaSAndroid Build Coastguard Worker pic = recorder.finishRecordingAsPicture();
740*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
741*c8dee2aaSAndroid Build Coastguard Worker }
DEF_TEST(Picture_UpdatedCull_2,r)742*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_UpdatedCull_2, r) {
743*c8dee2aaSAndroid Build Coastguard Worker SkRTreeFactory factory;
744*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
745*c8dee2aaSAndroid Build Coastguard Worker
746*c8dee2aaSAndroid Build Coastguard Worker auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
747*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
748*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
749*c8dee2aaSAndroid Build Coastguard Worker auto pic = recorder.finishRecordingAsPicture();
750*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
751*c8dee2aaSAndroid Build Coastguard Worker
752*c8dee2aaSAndroid Build Coastguard Worker canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
753*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
754*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
755*c8dee2aaSAndroid Build Coastguard Worker pic = recorder.finishRecordingAsPicture();
756*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
757*c8dee2aaSAndroid Build Coastguard Worker }
758*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Placeholder,r)759*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Placeholder, r) {
760*c8dee2aaSAndroid Build Coastguard Worker SkRect cull = { 0,0, 10,20 };
761*c8dee2aaSAndroid Build Coastguard Worker
762*c8dee2aaSAndroid Build Coastguard Worker // Each placeholder is unique.
763*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull),
764*c8dee2aaSAndroid Build Coastguard Worker p2 = SkPicture::MakePlaceholder(cull);
765*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect());
766*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, p1->cullRect() == cull);
767*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID());
768*c8dee2aaSAndroid Build Coastguard Worker
769*c8dee2aaSAndroid Build Coastguard Worker // Placeholders are never unrolled by SkCanvas (while other small pictures may be).
770*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
771*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(cull);
772*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPicture(p1);
773*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPicture(p2);
774*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
775*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, pic->approximateOpCount() == 2);
776*c8dee2aaSAndroid Build Coastguard Worker
777*c8dee2aaSAndroid Build Coastguard Worker // Any upper limit when recursing into nested placeholders is fine as long
778*c8dee2aaSAndroid Build Coastguard Worker // as it doesn't overflow an int.
779*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, pic->approximateOpCount(/*nested?*/true) >= 2);
780*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, pic->approximateOpCount(/*nested?*/true) <= 10);
781*c8dee2aaSAndroid Build Coastguard Worker }
782*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Picture_empty_serial,reporter)783*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_empty_serial, reporter) {
784*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder rec;
785*c8dee2aaSAndroid Build Coastguard Worker (void)rec.beginRecording(10, 10);
786*c8dee2aaSAndroid Build Coastguard Worker auto pic = rec.finishRecordingAsPicture();
787*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, pic);
788*c8dee2aaSAndroid Build Coastguard Worker
789*c8dee2aaSAndroid Build Coastguard Worker auto data = pic->serialize(); // explicitly testing the default SkSerialProcs
790*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, data);
791*c8dee2aaSAndroid Build Coastguard Worker
792*c8dee2aaSAndroid Build Coastguard Worker auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
793*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, pic2);
794*c8dee2aaSAndroid Build Coastguard Worker }
795*c8dee2aaSAndroid Build Coastguard Worker
796*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Picture_drawsNothing,r)797*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_drawsNothing, r) {
798*c8dee2aaSAndroid Build Coastguard Worker // Tests that pic->cullRect().isEmpty() is a good way to test a picture
799*c8dee2aaSAndroid Build Coastguard Worker // recorded with an R-tree draws nothing.
800*c8dee2aaSAndroid Build Coastguard Worker struct {
801*c8dee2aaSAndroid Build Coastguard Worker bool draws_nothing;
802*c8dee2aaSAndroid Build Coastguard Worker void (*fn)(SkCanvas*);
803*c8dee2aaSAndroid Build Coastguard Worker } cases[] = {
804*c8dee2aaSAndroid Build Coastguard Worker { true, [](SkCanvas* c) { } },
805*c8dee2aaSAndroid Build Coastguard Worker { true, [](SkCanvas* c) { c->save(); c->restore(); } },
806*c8dee2aaSAndroid Build Coastguard Worker { true, [](SkCanvas* c) { c->save(); c->clipRect({0,0,5,5}); c->restore(); } },
807*c8dee2aaSAndroid Build Coastguard Worker { true, [](SkCanvas* c) { c->clipRect({0,0,5,5}); } },
808*c8dee2aaSAndroid Build Coastguard Worker
809*c8dee2aaSAndroid Build Coastguard Worker { false, [](SkCanvas* c) { c->drawRect({0,0,5,5}, SkPaint{}); } },
810*c8dee2aaSAndroid Build Coastguard Worker { false, [](SkCanvas* c) { c->save(); c->drawRect({0,0,5,5}, SkPaint{}); c->restore(); } },
811*c8dee2aaSAndroid Build Coastguard Worker { false, [](SkCanvas* c) {
812*c8dee2aaSAndroid Build Coastguard Worker c->drawRect({0,0, 5, 5}, SkPaint{});
813*c8dee2aaSAndroid Build Coastguard Worker c->drawRect({5,5,10,10}, SkPaint{});
814*c8dee2aaSAndroid Build Coastguard Worker }},
815*c8dee2aaSAndroid Build Coastguard Worker };
816*c8dee2aaSAndroid Build Coastguard Worker
817*c8dee2aaSAndroid Build Coastguard Worker for (const auto& c : cases) {
818*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder rec;
819*c8dee2aaSAndroid Build Coastguard Worker SkRTreeFactory factory;
820*c8dee2aaSAndroid Build Coastguard Worker c.fn(rec.beginRecording(10,10, &factory));
821*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
822*c8dee2aaSAndroid Build Coastguard Worker
823*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, pic->cullRect().isEmpty() == c.draws_nothing);
824*c8dee2aaSAndroid Build Coastguard Worker }
825*c8dee2aaSAndroid Build Coastguard Worker }
826*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Picture_emptyNestedPictureBug,r)827*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_emptyNestedPictureBug, r) {
828*c8dee2aaSAndroid Build Coastguard Worker const SkRect bounds = {-5000, -5000, 5000, 5000};
829*c8dee2aaSAndroid Build Coastguard Worker
830*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
831*c8dee2aaSAndroid Build Coastguard Worker SkRTreeFactory factory;
832*c8dee2aaSAndroid Build Coastguard Worker
833*c8dee2aaSAndroid Build Coastguard Worker // These three pictures should all draw the same but due to bugs they don't:
834*c8dee2aaSAndroid Build Coastguard Worker //
835*c8dee2aaSAndroid Build Coastguard Worker // 1) inner has enough content that it is recoreded as an SkBigPicture,
836*c8dee2aaSAndroid Build Coastguard Worker // and all its content falls outside the positive/positive quadrant,
837*c8dee2aaSAndroid Build Coastguard Worker // and it is recorded with an R-tree so we contract the cullRect to those bounds;
838*c8dee2aaSAndroid Build Coastguard Worker //
839*c8dee2aaSAndroid Build Coastguard Worker // 2) middle wraps inner,
840*c8dee2aaSAndroid Build Coastguard Worker // and it its recorded with an R-tree so we update middle's cullRect to inner's;
841*c8dee2aaSAndroid Build Coastguard Worker //
842*c8dee2aaSAndroid Build Coastguard Worker // 3) outer wraps inner,
843*c8dee2aaSAndroid Build Coastguard Worker // and notices that middle contains only one op, drawPicture(inner),
844*c8dee2aaSAndroid Build Coastguard Worker // so it plays middle back during recording rather than ref'ing middle,
845*c8dee2aaSAndroid Build Coastguard Worker // querying middle's R-tree with its SkCanvas' bounds* {0,0, 5000,5000},
846*c8dee2aaSAndroid Build Coastguard Worker // finding nothing to draw.
847*c8dee2aaSAndroid Build Coastguard Worker //
848*c8dee2aaSAndroid Build Coastguard Worker // * The bug was that these bounds were not tracked as {-5000,-5000, 5000,5000}.
849*c8dee2aaSAndroid Build Coastguard Worker {
850*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
851*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(-100,-100);
852*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0,0,50,50}, SkPaint{});
853*c8dee2aaSAndroid Build Coastguard Worker }
854*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> inner = recorder.finishRecordingAsPicture();
855*c8dee2aaSAndroid Build Coastguard Worker
856*c8dee2aaSAndroid Build Coastguard Worker recorder.beginRecording(bounds, &factory)->drawPicture(inner);
857*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> middle = recorder.finishRecordingAsPicture();
858*c8dee2aaSAndroid Build Coastguard Worker
859*c8dee2aaSAndroid Build Coastguard Worker // This doesn't need &factory to reproduce the bug,
860*c8dee2aaSAndroid Build Coastguard Worker // but it's nice to see we come up with the same {-100,-100, -50,-50} bounds.
861*c8dee2aaSAndroid Build Coastguard Worker recorder.beginRecording(bounds, &factory)->drawPicture(middle);
862*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> outer = recorder.finishRecordingAsPicture();
863*c8dee2aaSAndroid Build Coastguard Worker
864*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, (inner ->cullRect() == SkRect{-100,-100, -50,-50}));
865*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, (middle->cullRect() == SkRect{-100,-100, -50,-50}));
866*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, (outer ->cullRect() == SkRect{-100,-100, -50,-50})); // Used to fail.
867*c8dee2aaSAndroid Build Coastguard Worker }
868*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Picture_fillsBBH,r)869*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_fillsBBH, r) {
870*c8dee2aaSAndroid Build Coastguard Worker // Test empty (0 draws), mini (1 draw), and big (2+) pictures, making sure they fill the BBH.
871*c8dee2aaSAndroid Build Coastguard Worker const SkRect rects[] = {
872*c8dee2aaSAndroid Build Coastguard Worker { 0, 0, 20,20},
873*c8dee2aaSAndroid Build Coastguard Worker {20,20, 40,40},
874*c8dee2aaSAndroid Build Coastguard Worker };
875*c8dee2aaSAndroid Build Coastguard Worker
876*c8dee2aaSAndroid Build Coastguard Worker for (int n = 0; n <= 2; n++) {
877*c8dee2aaSAndroid Build Coastguard Worker SkRTreeFactory factory;
878*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder rec;
879*c8dee2aaSAndroid Build Coastguard Worker
880*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkBBoxHierarchy> bbh = factory();
881*c8dee2aaSAndroid Build Coastguard Worker
882*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* c = rec.beginRecording({0,0, 100,100}, bbh);
883*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < n; i++) {
884*c8dee2aaSAndroid Build Coastguard Worker c->drawRect(rects[i], SkPaint{});
885*c8dee2aaSAndroid Build Coastguard Worker }
886*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
887*c8dee2aaSAndroid Build Coastguard Worker
888*c8dee2aaSAndroid Build Coastguard Worker std::vector<int> results;
889*c8dee2aaSAndroid Build Coastguard Worker bbh->search({0,0, 100,100}, &results);
890*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, (int)results.size() == n,
891*c8dee2aaSAndroid Build Coastguard Worker "results.size() == %d, want %d\n", (int)results.size(), n);
892*c8dee2aaSAndroid Build Coastguard Worker }
893*c8dee2aaSAndroid Build Coastguard Worker }
894*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Picture_nested_op_count,r)895*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Picture_nested_op_count, r) {
896*c8dee2aaSAndroid Build Coastguard Worker auto make_pic = [](int n, const sk_sp<SkPicture>& pic) {
897*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder rec;
898*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* c = rec.beginRecording({0,0, 100,100});
899*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < n; i++) {
900*c8dee2aaSAndroid Build Coastguard Worker if (pic) {
901*c8dee2aaSAndroid Build Coastguard Worker c->drawPicture(pic);
902*c8dee2aaSAndroid Build Coastguard Worker } else {
903*c8dee2aaSAndroid Build Coastguard Worker c->drawRect({0,0, 100,100}, SkPaint{});
904*c8dee2aaSAndroid Build Coastguard Worker }
905*c8dee2aaSAndroid Build Coastguard Worker }
906*c8dee2aaSAndroid Build Coastguard Worker return rec.finishRecordingAsPicture();
907*c8dee2aaSAndroid Build Coastguard Worker };
908*c8dee2aaSAndroid Build Coastguard Worker
909*c8dee2aaSAndroid Build Coastguard Worker auto check = [r](const sk_sp<SkPicture>& pic, int shallow, int nested) {
910*c8dee2aaSAndroid Build Coastguard Worker int s = pic->approximateOpCount(false);
911*c8dee2aaSAndroid Build Coastguard Worker int n = pic->approximateOpCount(true);
912*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, s == shallow);
913*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, n == nested);
914*c8dee2aaSAndroid Build Coastguard Worker };
915*c8dee2aaSAndroid Build Coastguard Worker
916*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> leaf1 = make_pic(1, nullptr);
917*c8dee2aaSAndroid Build Coastguard Worker check(leaf1, 1, 1);
918*c8dee2aaSAndroid Build Coastguard Worker
919*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> leaf10 = make_pic(10, nullptr);
920*c8dee2aaSAndroid Build Coastguard Worker check(leaf10, 10, 10);
921*c8dee2aaSAndroid Build Coastguard Worker
922*c8dee2aaSAndroid Build Coastguard Worker check(make_pic( 1, leaf1), 1, 1);
923*c8dee2aaSAndroid Build Coastguard Worker check(make_pic( 1, leaf10), 1, 10);
924*c8dee2aaSAndroid Build Coastguard Worker check(make_pic(10, leaf1), 10, 10);
925*c8dee2aaSAndroid Build Coastguard Worker check(make_pic(10, leaf10), 10, 100);
926*c8dee2aaSAndroid Build Coastguard Worker }
927