Flutter Engine
The Flutter Engine
CanvasStateTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
14#include "include/core/SkData.h"
21#include "include/core/SkRect.h"
33#include "src/base/SkTLazy.h"
34#include "tests/Test.h"
35
36#include <array>
37#include <cstdint>
38#include <cstring>
39
40class SkCanvasState;
41
42// Uncomment to include tests of CanvasState across a library boundary. This will change how 'dm'
43// is built so that the functions defined in CanvasStateHelpers do not exist inside 'dm', and are
44// instead compiled as part of the 'canvas_state_lib' build target. This produces a shared library
45// that must be passed to 'dm' using the --library flag when running.
46// #define SK_TEST_CANVAS_STATE_CROSS_LIBRARY
47
48// Must be included after SK_TEST_CANVAS_STATE_CROSS_LIBRARY is defined
50
51// dlopen, the library flag and canvas state helpers are only used for tests which require this flag
52#if defined(SK_TEST_CANVAS_STATE_CROSS_LIBRARY)
53
54static DEFINE_string(library, "",
55 "Support library to use for CanvasState test. Must be provided when"
56 " SK_TEST_CANVAS_STATE_CROSS_LIBRARY to specify the dynamically loaded library"
57 " that receives the captured canvas state. Functions from the library will be"
58 " called to test SkCanvasState. The library is built from the canvas_state_lib"
59 " target");
60
62
63// Automatically loads library passed to --library flag and closes it when it goes out of scope.
64class OpenLibResult {
65public:
66 OpenLibResult(skiatest::Reporter* reporter) {
67 if (FLAGS_library.count() == 1) {
68 fLibrary = SkLoadDynamicLibrary(FLAGS_library[0]);
69 REPORTER_ASSERT(reporter, fLibrary != nullptr, "Failed to open library!");
70 } else {
71 fLibrary = nullptr;
72 }
73 }
74
75 ~OpenLibResult() {
76 if (fLibrary) {
77 SkFreeDynamicLibrary(fLibrary);
78 }
79 }
80
81 // Load a function address from the library object, or null if the library had failed
82 void* procAddress(const char* funcName) {
83 if (fLibrary) {
84 return SkGetProcedureAddress(fLibrary, funcName);
85 }
86 return nullptr;
87 }
88
89private:
90 void* fLibrary;
91};
92
93#endif
94
95static void write_image(const SkImage* img, const char path[]) {
96 auto data = SkPngEncoder::Encode(nullptr, img, {});
97 SkFILEWStream(path).write(data->data(), data->size());
98}
99
100static void compare(skiatest::Reporter* reporter, SkImage* img0, SkImage* img1) {
101 if ((false)) {
102 static int counter;
103
104 SkDebugf("---- counter %d\n", counter);
106 name.printf("no_capture_%d.png", counter);
107 write_image(img0, name.c_str());
108 name.printf("capture_%d.png", counter);
109 write_image(img1, name.c_str());
110 counter++;
111 }
112
113 SkPixmap pm[2];
114 REPORTER_ASSERT(reporter, img0->peekPixels(&pm[0]));
115 REPORTER_ASSERT(reporter, img1->peekPixels(&pm[1]));
116 // now we memcmp the two bitmaps
117 REPORTER_ASSERT(reporter, pm[0].computeByteSize() == pm[1].computeByteSize());
118 REPORTER_ASSERT(reporter, pm[0].rowBytes() == (size_t)pm[0].width() * pm[0].info().bytesPerPixel());
119 REPORTER_ASSERT(reporter, pm[1].rowBytes() == (size_t)pm[1].width() * pm[1].info().bytesPerPixel());
120 if (memcmp(pm[0].addr(0, 0), pm[1].addr(0, 0), pm[0].computeByteSize()) != 0) {
122 }
123}
124
125DEF_TEST(CanvasState_test_complex_layers, reporter) {
126 const int WIDTH = 400;
127 const int HEIGHT = 400;
128 const int SPACER = 10;
129
131 SkIntToScalar(WIDTH-(2*SPACER)),
132 SkIntToScalar((HEIGHT-(2*SPACER)) / 7));
133
134 const SkColorType colorTypes[] = {
135 kRGB_565_SkColorType, kN32_SkColorType
136 };
137
138 const int layerAlpha[] = { 255, 255, 0 };
139
140 bool (*drawFn)(SkCanvasState* state, float l, float t,
141 float r, float b, int32_t s);
142
143#if defined(SK_TEST_CANVAS_STATE_CROSS_LIBRARY)
144 OpenLibResult openLibResult(reporter);
145 *(void**) (&drawFn) = openLibResult.procAddress("complex_layers_draw_from_canvas_state");
146#else
148#endif
149
150 REPORTER_ASSERT(reporter, drawFn);
151 if (!drawFn) {
152 return;
153 }
154
155 for (size_t i = 0; i < std::size(colorTypes); ++i) {
157 for (int j = 0; j < 2; ++j) {
158 auto surf = SkSurfaces::Raster(
160 SkCanvas* canvas = surf->getCanvas();
161
162 canvas->drawColor(SK_ColorRED);
163
164 for (size_t k = 0; k < std::size(layerAlpha); ++k) {
166 if (layerAlpha[k] != 0xFF) {
167 paint.init()->setAlpha(layerAlpha[k]);
168 }
169
170 // draw a rect within the layer's bounds and again outside the layer's bounds
171 canvas->saveLayer(SkCanvas::SaveLayerRec(&rect, paint.getMaybeNull()));
172
173 if (j) {
174 // Capture from the first Skia.
177
178 // And draw to it in the second Skia.
180 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, SPACER);
181 REPORTER_ASSERT(reporter, success);
182
183 // And release it in the *first* Skia.
185 } else {
186 // Draw in the first Skia.
187 complex_layers_draw(canvas, rect.fLeft, rect.fTop,
188 rect.fRight, rect.fBottom, SPACER);
189 }
190
191 canvas->restore();
192
193 // translate the canvas for the next iteration
194 canvas->translate(0, 2*(rect.height() + SPACER));
195 }
196 images[j] = surf->makeImageSnapshot();
197 }
198
199 compare(reporter, images[0].get(), images[1].get());
200 }
201}
202
203////////////////////////////////////////////////////////////////////////////////
204
205DEF_TEST(CanvasState_test_complex_clips, reporter) {
206 const int WIDTH = 400;
207 const int HEIGHT = 400;
208 const int SPACER = 10;
209
210 SkIRect layerRect = SkIRect::MakeWH(WIDTH, HEIGHT / 4);
211 layerRect.inset(2*SPACER, 2*SPACER);
212
213 SkIRect clipRect = layerRect;
214 clipRect.fRight = clipRect.fLeft + (clipRect.width() / 2) - (2*SPACER);
215 clipRect.outset(SPACER, SPACER);
216
217 SkIRect regionBounds = clipRect;
218 regionBounds.offset(clipRect.width() + (2*SPACER), 0);
219
220 SkIRect regionInterior = regionBounds;
221 regionInterior.inset(SPACER*3, SPACER*3);
222
223 SkRegion clipRegion;
224 clipRegion.setRect(regionBounds);
225 clipRegion.op(regionInterior, SkRegion::kDifference_Op);
226
227
228 const SkRegion::Op clipOps[] = { SkRegion::kIntersect_Op,
231 };
232
233 bool (*drawFn)(SkCanvasState* state, int32_t l, int32_t t,
234 int32_t r, int32_t b, int32_t clipOp,
235 int32_t regionRects, int32_t* rectCoords);
236
237#if defined(SK_TEST_CANVAS_STATE_CROSS_LIBRARY)
238 OpenLibResult openLibResult(reporter);
239 *(void**) (&drawFn) = openLibResult.procAddress("complex_clips_draw_from_canvas_state");
240#else
242#endif
243
244 REPORTER_ASSERT(reporter, drawFn);
245 if (!drawFn) {
246 return;
247 }
248
250 for (int i = 0; i < 2; ++i) {
252 SkCanvas* canvas = surf->getCanvas();
253
254 canvas->drawColor(SK_ColorRED);
255
256 SkRegion localRegion = clipRegion;
257
259 paint.setAlpha(128);
260 for (size_t j = 0; j < std::size(clipOps); ++j) {
261 SkRect layerBounds = SkRect::Make(layerRect);
262 canvas->saveLayer(SkCanvas::SaveLayerRec(&layerBounds, &paint));
263
264 if (i) {
267
268 SkRegion::Iterator iter(localRegion);
269 SkTDArray<int32_t> rectCoords;
270 for (; !iter.done(); iter.next()) {
271 const SkIRect& rect = iter.rect();
272 *rectCoords.append() = rect.fLeft;
273 *rectCoords.append() = rect.fTop;
274 *rectCoords.append() = rect.fRight;
275 *rectCoords.append() = rect.fBottom;
276 }
277 bool success = drawFn(state, clipRect.fLeft, clipRect.fTop,
278 clipRect.fRight, clipRect.fBottom, clipOps[j],
279 rectCoords.size() / 4, rectCoords.begin());
280 REPORTER_ASSERT(reporter, success);
281
283 } else {
284 complex_clips_draw(canvas, clipRect.fLeft, clipRect.fTop,
285 clipRect.fRight, clipRect.fBottom, clipOps[j],
286 localRegion);
287 }
288
289 canvas->restore();
290
291 // translate the canvas and region for the next iteration
292 canvas->translate(0, SkIntToScalar(2*(layerRect.height() + (SPACER))));
293 localRegion.translate(0, 2*(layerRect.height() + SPACER));
294 }
295 images[i] = surf->makeImageSnapshot();
296 }
297
298 compare(reporter, images[0].get(), images[1].get());
299}
300
301////////////////////////////////////////////////////////////////////////////////
302
303DEF_TEST(CanvasState_test_soft_clips, reporter) {
305 bitmap.allocN32Pixels(10, 10);
306 SkCanvas canvas(bitmap);
307
308 SkRRect roundRect;
309 roundRect.setOval(SkRect::MakeWH(5, 5));
310
311 canvas.clipRRect(roundRect, SkClipOp::kIntersect, true);
312
315}
316
317DEF_TEST(CanvasState_test_saveLayer_clip, reporter) {
318 const int WIDTH = 100;
319 const int HEIGHT = 100;
320 const int LAYER_WIDTH = 50;
321 const int LAYER_HEIGHT = 50;
322
324 bitmap.allocN32Pixels(WIDTH, HEIGHT);
325 SkCanvas canvas(bitmap);
326
327 SkRect bounds = SkRect::MakeWH(SkIntToScalar(LAYER_WIDTH), SkIntToScalar(LAYER_HEIGHT));
329
330 // Check that saveLayer sets the clip stack to the layer bounds.
331 canvas.saveLayer(&bounds, nullptr);
332 SkIRect devClip = canvas.getDeviceClipBounds();
334 REPORTER_ASSERT(reporter, devClip.width() == LAYER_WIDTH);
335 REPORTER_ASSERT(reporter, devClip.height() == LAYER_HEIGHT);
336 canvas.restore();
337}
bool complex_layers_draw_from_canvas_state(SkCanvasState *state, float left, float top, float right, float bottom, int32_t spacer)
void complex_layers_draw(SkCanvas *canvas, float left, float top, float right, float bottom, int32_t spacer)
bool complex_clips_draw_from_canvas_state(SkCanvasState *state, int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t clipOp, int32_t regionRects, int32_t *rectCoords)
void complex_clips_draw(SkCanvas *canvas, int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t clipOp, const SkRegion &localRegion)
static void write_image(const SkImage *img, const char path[])
DEF_TEST(CanvasState_test_complex_layers, reporter)
static void compare(skiatest::Reporter *reporter, SkImage *img0, SkImage *img1)
#define DEFINE_string(name, defaultValue, helpString)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
reporter
Definition: FontMgrTest.cpp:39
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
SkColorType
Definition: SkColorType.h:19
@ kRGB_565_SkColorType
pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word
Definition: SkColorType.h:22
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
void * SkGetProcedureAddress(void *library, const char *functionName)
void * SkLoadDynamicLibrary(const char *libraryName)
bool SkFreeDynamicLibrary(void *library)
#define SkIntToScalar(x)
Definition: SkScalar.h:57
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define WIDTH
#define HEIGHT
static SkCanvasState * CaptureCanvasState(SkCanvas *canvas)
static void ReleaseCanvasState(SkCanvasState *state)
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void clipRect(const SkRect &rect, SkClipOp op, bool doAntiAlias)
Definition: SkCanvas.cpp:1361
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition: SkCanvas.h:1182
virtual bool isClipRect() const
Definition: SkCanvas.cpp:1553
SkIRect getDeviceClipBounds() const
Definition: SkCanvas.cpp:1607
void clipRRect(const SkRRect &rrect, SkClipOp op, bool doAntiAlias)
Definition: SkCanvas.cpp:1439
bool write(const void *buffer, size_t size) override
Definition: SkStream.cpp:426
bool peekPixels(SkPixmap *pixmap) const
Definition: SkImage.cpp:34
void setOval(const SkRect &oval)
Definition: SkRRect.cpp:30
const SkIRect & rect() const
Definition: SkRegion.h:501
bool done() const
Definition: SkRegion.h:488
void translate(int dx, int dy)
Definition: SkRegion.h:349
@ kIntersect_Op
target intersected with operand
Definition: SkRegion.h:368
@ kDifference_Op
target minus operand
Definition: SkRegion.h:367
bool op(const SkIRect &rect, Op op)
Definition: SkRegion.h:384
bool setRect(const SkIRect &rect)
Definition: SkRegion.cpp:192
int size() const
Definition: SkTDArray.h:138
T * begin()
Definition: SkTDArray.h:150
T * append()
Definition: SkTDArray.h:191
const Paint & paint
Definition: color_source.cc:38
static bool b
struct MyStruct s
AtkStateType state
std::array< MockImage, 3 > images
Definition: mock_vulkan.cc:41
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
Optional< SkRect > bounds
Definition: SkRecords.h:189
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
Definition: bitmap.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
const myers::Point & get(const myers::Segment &)
int32_t width
Definition: SkRect.h:32
void inset(int32_t dx, int32_t dy)
Definition: SkRect.h:411
constexpr int32_t height() const
Definition: SkRect.h:165
constexpr int32_t width() const
Definition: SkRect.h:158
void offset(int32_t dx, int32_t dy)
Definition: SkRect.h:367
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
static SkImageInfo MakeN32Premul(int width, int height)
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63