xref: /aosp_15_r20/external/skia/tests/CanvasStateTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2013 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/SkAlphaType.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/SkColorType.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkDataTable.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixmap.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRegion.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkPngEncoder.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTDArray.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "include/utils/SkCanvasStateUtils.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTLazy.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
35*c8dee2aaSAndroid Build Coastguard Worker 
36*c8dee2aaSAndroid Build Coastguard Worker #include <array>
37*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
38*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
39*c8dee2aaSAndroid Build Coastguard Worker 
40*c8dee2aaSAndroid Build Coastguard Worker class SkCanvasState;
41*c8dee2aaSAndroid Build Coastguard Worker 
42*c8dee2aaSAndroid Build Coastguard Worker // Uncomment to include tests of CanvasState across a library boundary. This will change how 'dm'
43*c8dee2aaSAndroid Build Coastguard Worker // is built so that the functions defined in CanvasStateHelpers do not exist inside 'dm', and are
44*c8dee2aaSAndroid Build Coastguard Worker // instead compiled as part of the 'canvas_state_lib' build target. This produces a shared library
45*c8dee2aaSAndroid Build Coastguard Worker // that must be passed to 'dm' using the --library flag when running.
46*c8dee2aaSAndroid Build Coastguard Worker // #define SK_TEST_CANVAS_STATE_CROSS_LIBRARY
47*c8dee2aaSAndroid Build Coastguard Worker 
48*c8dee2aaSAndroid Build Coastguard Worker // Must be included after SK_TEST_CANVAS_STATE_CROSS_LIBRARY is defined
49*c8dee2aaSAndroid Build Coastguard Worker #include "tests/CanvasStateHelpers.h"
50*c8dee2aaSAndroid Build Coastguard Worker 
51*c8dee2aaSAndroid Build Coastguard Worker // dlopen, the library flag and canvas state helpers are only used for tests which require this flag
52*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_TEST_CANVAS_STATE_CROSS_LIBRARY)
53*c8dee2aaSAndroid Build Coastguard Worker 
54*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(library, "",
55*c8dee2aaSAndroid Build Coastguard Worker                      "Support library to use for CanvasState test. Must be provided when"
56*c8dee2aaSAndroid Build Coastguard Worker                      " SK_TEST_CANVAS_STATE_CROSS_LIBRARY to specify the dynamically loaded library"
57*c8dee2aaSAndroid Build Coastguard Worker                      " that receives the captured canvas state. Functions from the library will be"
58*c8dee2aaSAndroid Build Coastguard Worker                      " called to test SkCanvasState. The library is built from the canvas_state_lib"
59*c8dee2aaSAndroid Build Coastguard Worker                      " target");
60*c8dee2aaSAndroid Build Coastguard Worker 
61*c8dee2aaSAndroid Build Coastguard Worker #include "tools/library/LoadDynamicLibrary.h"
62*c8dee2aaSAndroid Build Coastguard Worker 
63*c8dee2aaSAndroid Build Coastguard Worker // Automatically loads library passed to --library flag and closes it when it goes out of scope.
64*c8dee2aaSAndroid Build Coastguard Worker class OpenLibResult {
65*c8dee2aaSAndroid Build Coastguard Worker public:
OpenLibResult(skiatest::Reporter * reporter)66*c8dee2aaSAndroid Build Coastguard Worker     OpenLibResult(skiatest::Reporter* reporter) {
67*c8dee2aaSAndroid Build Coastguard Worker         if (FLAGS_library.count() == 1) {
68*c8dee2aaSAndroid Build Coastguard Worker             fLibrary = SkLoadDynamicLibrary(FLAGS_library[0]);
69*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, fLibrary != nullptr, "Failed to open library!");
70*c8dee2aaSAndroid Build Coastguard Worker         } else {
71*c8dee2aaSAndroid Build Coastguard Worker             fLibrary = nullptr;
72*c8dee2aaSAndroid Build Coastguard Worker         }
73*c8dee2aaSAndroid Build Coastguard Worker     }
74*c8dee2aaSAndroid Build Coastguard Worker 
~OpenLibResult()75*c8dee2aaSAndroid Build Coastguard Worker     ~OpenLibResult() {
76*c8dee2aaSAndroid Build Coastguard Worker         if (fLibrary) {
77*c8dee2aaSAndroid Build Coastguard Worker             SkFreeDynamicLibrary(fLibrary);
78*c8dee2aaSAndroid Build Coastguard Worker         }
79*c8dee2aaSAndroid Build Coastguard Worker     }
80*c8dee2aaSAndroid Build Coastguard Worker 
81*c8dee2aaSAndroid Build Coastguard Worker     // Load a function address from the library object, or null if the library had failed
procAddress(const char * funcName)82*c8dee2aaSAndroid Build Coastguard Worker     void* procAddress(const char* funcName) {
83*c8dee2aaSAndroid Build Coastguard Worker         if (fLibrary) {
84*c8dee2aaSAndroid Build Coastguard Worker             return SkGetProcedureAddress(fLibrary, funcName);
85*c8dee2aaSAndroid Build Coastguard Worker         }
86*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
87*c8dee2aaSAndroid Build Coastguard Worker     }
88*c8dee2aaSAndroid Build Coastguard Worker 
89*c8dee2aaSAndroid Build Coastguard Worker private:
90*c8dee2aaSAndroid Build Coastguard Worker     void* fLibrary;
91*c8dee2aaSAndroid Build Coastguard Worker };
92*c8dee2aaSAndroid Build Coastguard Worker 
93*c8dee2aaSAndroid Build Coastguard Worker #endif
94*c8dee2aaSAndroid Build Coastguard Worker 
write_image(const SkImage * img,const char path[])95*c8dee2aaSAndroid Build Coastguard Worker static void write_image(const SkImage* img, const char path[]) {
96*c8dee2aaSAndroid Build Coastguard Worker     auto data = SkPngEncoder::Encode(nullptr, img, {});
97*c8dee2aaSAndroid Build Coastguard Worker     SkFILEWStream(path).write(data->data(), data->size());
98*c8dee2aaSAndroid Build Coastguard Worker }
99*c8dee2aaSAndroid Build Coastguard Worker 
compare(skiatest::Reporter * reporter,SkImage * img0,SkImage * img1)100*c8dee2aaSAndroid Build Coastguard Worker static void compare(skiatest::Reporter* reporter, SkImage* img0, SkImage* img1) {
101*c8dee2aaSAndroid Build Coastguard Worker     if ((false)) {
102*c8dee2aaSAndroid Build Coastguard Worker         static int counter;
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("---- counter %d\n", counter);
105*c8dee2aaSAndroid Build Coastguard Worker         SkString name;
106*c8dee2aaSAndroid Build Coastguard Worker         name.printf("no_capture_%d.png", counter);
107*c8dee2aaSAndroid Build Coastguard Worker         write_image(img0, name.c_str());
108*c8dee2aaSAndroid Build Coastguard Worker         name.printf("capture_%d.png", counter);
109*c8dee2aaSAndroid Build Coastguard Worker         write_image(img1, name.c_str());
110*c8dee2aaSAndroid Build Coastguard Worker         counter++;
111*c8dee2aaSAndroid Build Coastguard Worker     }
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap pm[2];
114*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, img0->peekPixels(&pm[0]));
115*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, img1->peekPixels(&pm[1]));
116*c8dee2aaSAndroid Build Coastguard Worker     // now we memcmp the two bitmaps
117*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, pm[0].computeByteSize() == pm[1].computeByteSize());
118*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, pm[0].rowBytes() == (size_t)pm[0].width() * pm[0].info().bytesPerPixel());
119*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, pm[1].rowBytes() == (size_t)pm[1].width() * pm[1].info().bytesPerPixel());
120*c8dee2aaSAndroid Build Coastguard Worker     if (memcmp(pm[0].addr(0, 0), pm[1].addr(0, 0), pm[0].computeByteSize()) != 0) {
121*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, false);
122*c8dee2aaSAndroid Build Coastguard Worker     }
123*c8dee2aaSAndroid Build Coastguard Worker }
124*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(CanvasState_test_complex_layers,reporter)125*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(CanvasState_test_complex_layers, reporter) {
126*c8dee2aaSAndroid Build Coastguard Worker     const int WIDTH = 400;
127*c8dee2aaSAndroid Build Coastguard Worker     const int HEIGHT = 400;
128*c8dee2aaSAndroid Build Coastguard Worker     const int SPACER = 10;
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = SkRect::MakeXYWH(SkIntToScalar(SPACER), SkIntToScalar(SPACER),
131*c8dee2aaSAndroid Build Coastguard Worker                                    SkIntToScalar(WIDTH-(2*SPACER)),
132*c8dee2aaSAndroid Build Coastguard Worker                                    SkIntToScalar((HEIGHT-(2*SPACER)) / 7));
133*c8dee2aaSAndroid Build Coastguard Worker 
134*c8dee2aaSAndroid Build Coastguard Worker     const SkColorType colorTypes[] = {
135*c8dee2aaSAndroid Build Coastguard Worker         kRGB_565_SkColorType, kN32_SkColorType
136*c8dee2aaSAndroid Build Coastguard Worker     };
137*c8dee2aaSAndroid Build Coastguard Worker 
138*c8dee2aaSAndroid Build Coastguard Worker     const int layerAlpha[] = { 255, 255, 0 };
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker     bool (*drawFn)(SkCanvasState* state, float l, float t,
141*c8dee2aaSAndroid Build Coastguard Worker                    float r, float b, int32_t s);
142*c8dee2aaSAndroid Build Coastguard Worker 
143*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_TEST_CANVAS_STATE_CROSS_LIBRARY)
144*c8dee2aaSAndroid Build Coastguard Worker     OpenLibResult openLibResult(reporter);
145*c8dee2aaSAndroid Build Coastguard Worker     *(void**) (&drawFn) = openLibResult.procAddress("complex_layers_draw_from_canvas_state");
146*c8dee2aaSAndroid Build Coastguard Worker #else
147*c8dee2aaSAndroid Build Coastguard Worker     drawFn = complex_layers_draw_from_canvas_state;
148*c8dee2aaSAndroid Build Coastguard Worker #endif
149*c8dee2aaSAndroid Build Coastguard Worker 
150*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, drawFn);
151*c8dee2aaSAndroid Build Coastguard Worker     if (!drawFn) {
152*c8dee2aaSAndroid Build Coastguard Worker         return;
153*c8dee2aaSAndroid Build Coastguard Worker     }
154*c8dee2aaSAndroid Build Coastguard Worker 
155*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < std::size(colorTypes); ++i) {
156*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkImage> images[2];
157*c8dee2aaSAndroid Build Coastguard Worker         for (int j = 0; j < 2; ++j) {
158*c8dee2aaSAndroid Build Coastguard Worker             auto surf = SkSurfaces::Raster(
159*c8dee2aaSAndroid Build Coastguard Worker                     SkImageInfo::Make(WIDTH, HEIGHT, colorTypes[i], kPremul_SkAlphaType));
160*c8dee2aaSAndroid Build Coastguard Worker             SkCanvas* canvas = surf->getCanvas();
161*c8dee2aaSAndroid Build Coastguard Worker 
162*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawColor(SK_ColorRED);
163*c8dee2aaSAndroid Build Coastguard Worker 
164*c8dee2aaSAndroid Build Coastguard Worker             for (size_t k = 0; k < std::size(layerAlpha); ++k) {
165*c8dee2aaSAndroid Build Coastguard Worker                 SkTLazy<SkPaint> paint;
166*c8dee2aaSAndroid Build Coastguard Worker                 if (layerAlpha[k] != 0xFF) {
167*c8dee2aaSAndroid Build Coastguard Worker                     paint.init()->setAlpha(layerAlpha[k]);
168*c8dee2aaSAndroid Build Coastguard Worker                 }
169*c8dee2aaSAndroid Build Coastguard Worker 
170*c8dee2aaSAndroid Build Coastguard Worker                 // draw a rect within the layer's bounds and again outside the layer's bounds
171*c8dee2aaSAndroid Build Coastguard Worker                 canvas->saveLayer(SkCanvas::SaveLayerRec(&rect, paint.getMaybeNull()));
172*c8dee2aaSAndroid Build Coastguard Worker 
173*c8dee2aaSAndroid Build Coastguard Worker                 if (j) {
174*c8dee2aaSAndroid Build Coastguard Worker                     // Capture from the first Skia.
175*c8dee2aaSAndroid Build Coastguard Worker                     SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(canvas);
176*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(reporter, state);
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker                     // And draw to it in the second Skia.
179*c8dee2aaSAndroid Build Coastguard Worker                     bool success = complex_layers_draw_from_canvas_state(state,
180*c8dee2aaSAndroid Build Coastguard Worker                             rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, SPACER);
181*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(reporter, success);
182*c8dee2aaSAndroid Build Coastguard Worker 
183*c8dee2aaSAndroid Build Coastguard Worker                     // And release it in the *first* Skia.
184*c8dee2aaSAndroid Build Coastguard Worker                     SkCanvasStateUtils::ReleaseCanvasState(state);
185*c8dee2aaSAndroid Build Coastguard Worker                 } else {
186*c8dee2aaSAndroid Build Coastguard Worker                     // Draw in the first Skia.
187*c8dee2aaSAndroid Build Coastguard Worker                     complex_layers_draw(canvas, rect.fLeft, rect.fTop,
188*c8dee2aaSAndroid Build Coastguard Worker                                         rect.fRight, rect.fBottom, SPACER);
189*c8dee2aaSAndroid Build Coastguard Worker                 }
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker                 canvas->restore();
192*c8dee2aaSAndroid Build Coastguard Worker 
193*c8dee2aaSAndroid Build Coastguard Worker                 // translate the canvas for the next iteration
194*c8dee2aaSAndroid Build Coastguard Worker                 canvas->translate(0, 2*(rect.height() + SPACER));
195*c8dee2aaSAndroid Build Coastguard Worker             }
196*c8dee2aaSAndroid Build Coastguard Worker             images[j] = surf->makeImageSnapshot();
197*c8dee2aaSAndroid Build Coastguard Worker         }
198*c8dee2aaSAndroid Build Coastguard Worker 
199*c8dee2aaSAndroid Build Coastguard Worker         compare(reporter, images[0].get(), images[1].get());
200*c8dee2aaSAndroid Build Coastguard Worker     }
201*c8dee2aaSAndroid Build Coastguard Worker }
202*c8dee2aaSAndroid Build Coastguard Worker 
203*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
204*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(CanvasState_test_complex_clips,reporter)205*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(CanvasState_test_complex_clips, reporter) {
206*c8dee2aaSAndroid Build Coastguard Worker     const int WIDTH = 400;
207*c8dee2aaSAndroid Build Coastguard Worker     const int HEIGHT = 400;
208*c8dee2aaSAndroid Build Coastguard Worker     const int SPACER = 10;
209*c8dee2aaSAndroid Build Coastguard Worker 
210*c8dee2aaSAndroid Build Coastguard Worker     SkIRect layerRect = SkIRect::MakeWH(WIDTH, HEIGHT / 4);
211*c8dee2aaSAndroid Build Coastguard Worker     layerRect.inset(2*SPACER, 2*SPACER);
212*c8dee2aaSAndroid Build Coastguard Worker 
213*c8dee2aaSAndroid Build Coastguard Worker     SkIRect clipRect = layerRect;
214*c8dee2aaSAndroid Build Coastguard Worker     clipRect.fRight = clipRect.fLeft + (clipRect.width() / 2) - (2*SPACER);
215*c8dee2aaSAndroid Build Coastguard Worker     clipRect.outset(SPACER, SPACER);
216*c8dee2aaSAndroid Build Coastguard Worker 
217*c8dee2aaSAndroid Build Coastguard Worker     SkIRect regionBounds = clipRect;
218*c8dee2aaSAndroid Build Coastguard Worker     regionBounds.offset(clipRect.width() + (2*SPACER), 0);
219*c8dee2aaSAndroid Build Coastguard Worker 
220*c8dee2aaSAndroid Build Coastguard Worker     SkIRect regionInterior = regionBounds;
221*c8dee2aaSAndroid Build Coastguard Worker     regionInterior.inset(SPACER*3, SPACER*3);
222*c8dee2aaSAndroid Build Coastguard Worker 
223*c8dee2aaSAndroid Build Coastguard Worker     SkRegion clipRegion;
224*c8dee2aaSAndroid Build Coastguard Worker     clipRegion.setRect(regionBounds);
225*c8dee2aaSAndroid Build Coastguard Worker     clipRegion.op(regionInterior, SkRegion::kDifference_Op);
226*c8dee2aaSAndroid Build Coastguard Worker 
227*c8dee2aaSAndroid Build Coastguard Worker 
228*c8dee2aaSAndroid Build Coastguard Worker     const SkRegion::Op clipOps[] = { SkRegion::kIntersect_Op,
229*c8dee2aaSAndroid Build Coastguard Worker                                      SkRegion::kIntersect_Op,
230*c8dee2aaSAndroid Build Coastguard Worker                                      SkRegion::kDifference_Op,
231*c8dee2aaSAndroid Build Coastguard Worker     };
232*c8dee2aaSAndroid Build Coastguard Worker 
233*c8dee2aaSAndroid Build Coastguard Worker     bool (*drawFn)(SkCanvasState* state, int32_t l, int32_t t,
234*c8dee2aaSAndroid Build Coastguard Worker                    int32_t r, int32_t b, int32_t clipOp,
235*c8dee2aaSAndroid Build Coastguard Worker                    int32_t regionRects, int32_t* rectCoords);
236*c8dee2aaSAndroid Build Coastguard Worker 
237*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_TEST_CANVAS_STATE_CROSS_LIBRARY)
238*c8dee2aaSAndroid Build Coastguard Worker     OpenLibResult openLibResult(reporter);
239*c8dee2aaSAndroid Build Coastguard Worker     *(void**) (&drawFn) = openLibResult.procAddress("complex_clips_draw_from_canvas_state");
240*c8dee2aaSAndroid Build Coastguard Worker #else
241*c8dee2aaSAndroid Build Coastguard Worker     drawFn = complex_clips_draw_from_canvas_state;
242*c8dee2aaSAndroid Build Coastguard Worker #endif
243*c8dee2aaSAndroid Build Coastguard Worker 
244*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, drawFn);
245*c8dee2aaSAndroid Build Coastguard Worker     if (!drawFn) {
246*c8dee2aaSAndroid Build Coastguard Worker         return;
247*c8dee2aaSAndroid Build Coastguard Worker     }
248*c8dee2aaSAndroid Build Coastguard Worker 
249*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> images[2];
250*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < 2; ++i) {
251*c8dee2aaSAndroid Build Coastguard Worker         auto surf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(WIDTH, HEIGHT));
252*c8dee2aaSAndroid Build Coastguard Worker         SkCanvas* canvas = surf->getCanvas();
253*c8dee2aaSAndroid Build Coastguard Worker 
254*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawColor(SK_ColorRED);
255*c8dee2aaSAndroid Build Coastguard Worker 
256*c8dee2aaSAndroid Build Coastguard Worker         SkRegion localRegion = clipRegion;
257*c8dee2aaSAndroid Build Coastguard Worker 
258*c8dee2aaSAndroid Build Coastguard Worker         SkPaint paint;
259*c8dee2aaSAndroid Build Coastguard Worker         paint.setAlpha(128);
260*c8dee2aaSAndroid Build Coastguard Worker         for (size_t j = 0; j < std::size(clipOps); ++j) {
261*c8dee2aaSAndroid Build Coastguard Worker             SkRect layerBounds = SkRect::Make(layerRect);
262*c8dee2aaSAndroid Build Coastguard Worker             canvas->saveLayer(SkCanvas::SaveLayerRec(&layerBounds, &paint));
263*c8dee2aaSAndroid Build Coastguard Worker 
264*c8dee2aaSAndroid Build Coastguard Worker             if (i) {
265*c8dee2aaSAndroid Build Coastguard Worker                 SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(canvas);
266*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(reporter, state);
267*c8dee2aaSAndroid Build Coastguard Worker 
268*c8dee2aaSAndroid Build Coastguard Worker                 SkRegion::Iterator iter(localRegion);
269*c8dee2aaSAndroid Build Coastguard Worker                 SkTDArray<int32_t> rectCoords;
270*c8dee2aaSAndroid Build Coastguard Worker                 for (; !iter.done(); iter.next()) {
271*c8dee2aaSAndroid Build Coastguard Worker                     const SkIRect& rect = iter.rect();
272*c8dee2aaSAndroid Build Coastguard Worker                     *rectCoords.append() = rect.fLeft;
273*c8dee2aaSAndroid Build Coastguard Worker                     *rectCoords.append() = rect.fTop;
274*c8dee2aaSAndroid Build Coastguard Worker                     *rectCoords.append() = rect.fRight;
275*c8dee2aaSAndroid Build Coastguard Worker                     *rectCoords.append() = rect.fBottom;
276*c8dee2aaSAndroid Build Coastguard Worker                 }
277*c8dee2aaSAndroid Build Coastguard Worker                 bool success = drawFn(state, clipRect.fLeft, clipRect.fTop,
278*c8dee2aaSAndroid Build Coastguard Worker                                       clipRect.fRight, clipRect.fBottom, clipOps[j],
279*c8dee2aaSAndroid Build Coastguard Worker                                       rectCoords.size() / 4, rectCoords.begin());
280*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(reporter, success);
281*c8dee2aaSAndroid Build Coastguard Worker 
282*c8dee2aaSAndroid Build Coastguard Worker                 SkCanvasStateUtils::ReleaseCanvasState(state);
283*c8dee2aaSAndroid Build Coastguard Worker             } else {
284*c8dee2aaSAndroid Build Coastguard Worker                 complex_clips_draw(canvas, clipRect.fLeft, clipRect.fTop,
285*c8dee2aaSAndroid Build Coastguard Worker                                    clipRect.fRight, clipRect.fBottom, clipOps[j],
286*c8dee2aaSAndroid Build Coastguard Worker                                    localRegion);
287*c8dee2aaSAndroid Build Coastguard Worker             }
288*c8dee2aaSAndroid Build Coastguard Worker 
289*c8dee2aaSAndroid Build Coastguard Worker             canvas->restore();
290*c8dee2aaSAndroid Build Coastguard Worker 
291*c8dee2aaSAndroid Build Coastguard Worker             // translate the canvas and region for the next iteration
292*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(0, SkIntToScalar(2*(layerRect.height() + (SPACER))));
293*c8dee2aaSAndroid Build Coastguard Worker             localRegion.translate(0, 2*(layerRect.height() + SPACER));
294*c8dee2aaSAndroid Build Coastguard Worker         }
295*c8dee2aaSAndroid Build Coastguard Worker         images[i] = surf->makeImageSnapshot();
296*c8dee2aaSAndroid Build Coastguard Worker     }
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker     compare(reporter, images[0].get(), images[1].get());
299*c8dee2aaSAndroid Build Coastguard Worker }
300*c8dee2aaSAndroid Build Coastguard Worker 
301*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
302*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(CanvasState_test_soft_clips,reporter)303*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(CanvasState_test_soft_clips, reporter) {
304*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bitmap;
305*c8dee2aaSAndroid Build Coastguard Worker     bitmap.allocN32Pixels(10, 10);
306*c8dee2aaSAndroid Build Coastguard Worker     SkCanvas canvas(bitmap);
307*c8dee2aaSAndroid Build Coastguard Worker 
308*c8dee2aaSAndroid Build Coastguard Worker     SkRRect roundRect;
309*c8dee2aaSAndroid Build Coastguard Worker     roundRect.setOval(SkRect::MakeWH(5, 5));
310*c8dee2aaSAndroid Build Coastguard Worker 
311*c8dee2aaSAndroid Build Coastguard Worker     canvas.clipRRect(roundRect, SkClipOp::kIntersect, true);
312*c8dee2aaSAndroid Build Coastguard Worker 
313*c8dee2aaSAndroid Build Coastguard Worker     SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
314*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, !state);
315*c8dee2aaSAndroid Build Coastguard Worker }
316*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(CanvasState_test_saveLayer_clip,reporter)317*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(CanvasState_test_saveLayer_clip, reporter) {
318*c8dee2aaSAndroid Build Coastguard Worker     const int WIDTH = 100;
319*c8dee2aaSAndroid Build Coastguard Worker     const int HEIGHT = 100;
320*c8dee2aaSAndroid Build Coastguard Worker     const int LAYER_WIDTH = 50;
321*c8dee2aaSAndroid Build Coastguard Worker     const int LAYER_HEIGHT = 50;
322*c8dee2aaSAndroid Build Coastguard Worker 
323*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bitmap;
324*c8dee2aaSAndroid Build Coastguard Worker     bitmap.allocN32Pixels(WIDTH, HEIGHT);
325*c8dee2aaSAndroid Build Coastguard Worker     SkCanvas canvas(bitmap);
326*c8dee2aaSAndroid Build Coastguard Worker 
327*c8dee2aaSAndroid Build Coastguard Worker     SkRect bounds = SkRect::MakeWH(SkIntToScalar(LAYER_WIDTH), SkIntToScalar(LAYER_HEIGHT));
328*c8dee2aaSAndroid Build Coastguard Worker     canvas.clipRect(SkRect::MakeWH(SkIntToScalar(WIDTH), SkIntToScalar(HEIGHT)));
329*c8dee2aaSAndroid Build Coastguard Worker 
330*c8dee2aaSAndroid Build Coastguard Worker     // Check that saveLayer sets the clip stack to the layer bounds.
331*c8dee2aaSAndroid Build Coastguard Worker     canvas.saveLayer(&bounds, nullptr);
332*c8dee2aaSAndroid Build Coastguard Worker     SkIRect devClip = canvas.getDeviceClipBounds();
333*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, canvas.isClipRect());
334*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, devClip.width() == LAYER_WIDTH);
335*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, devClip.height() == LAYER_HEIGHT);
336*c8dee2aaSAndroid Build Coastguard Worker     canvas.restore();
337*c8dee2aaSAndroid Build Coastguard Worker }
338