• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkImageInfo.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkRRect.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRegion.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkTypes.h"
18 #include "include/private/SkTDArray.h"
19 #include "include/utils/SkCanvasStateUtils.h"
20 #include "src/core/SkCanvasPriv.h"
21 #include "src/core/SkClipOpPriv.h"
22 #include "src/core/SkTLazy.h"
23 #include "tests/CanvasStateHelpers.h"
24 #include "tests/Test.h"
25 #include "tools/flags/CommandLineFlags.h"
26 
27 #include <cstring>
28 
29 class SkCanvasState;
30 
31 // dlopen and the library flag are only used for tests which require this flag.
32 #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
33 #include <dlfcn.h>
34 
35 static DEFINE_string(library, "",
36                      "Support library to use for CanvasState test. If empty (the default), "
37                      "the test will be run without crossing a library boundary. Otherwise, "
38                      "it is expected to be a full path to a shared library file, which will"
39                      " be dynamically loaded. Functions from the library will be called to "
40                      "test SkCanvasState. Instructions for generating the library are in "
41                      "gyp/canvas_state_lib.gyp");
42 
43 
44 // This class calls dlopen on the library passed in to the command line flag library, and handles
45 // calling dlclose when it goes out of scope.
46 class OpenLibResult {
47 public:
48     // If the flag library was passed to this run of the test, attempt to open it using dlopen and
49     // report whether it succeeded.
OpenLibResult(skiatest::Reporter * reporter)50     OpenLibResult(skiatest::Reporter* reporter) {
51         if (FLAGS_library.count() == 1) {
52             fHandle = dlopen(FLAGS_library[0], RTLD_LAZY | RTLD_LOCAL);
53             REPORTER_ASSERT(reporter, fHandle != nullptr, "Failed to open library!");
54         } else {
55             fHandle = nullptr;
56         }
57     }
58 
59     // Automatically call dlclose when going out of scope.
~OpenLibResult()60     ~OpenLibResult() {
61         if (fHandle) {
62             dlclose(fHandle);
63         }
64     }
65 
66     // Pointer to the shared library object.
handle()67     void* handle() { return fHandle; }
68 
69 private:
70     void* fHandle;
71 };
72 
DEF_TEST(CanvasState_test_complex_layers,reporter)73 DEF_TEST(CanvasState_test_complex_layers, reporter) {
74     const int WIDTH = 400;
75     const int HEIGHT = 400;
76     const int SPACER = 10;
77 
78     SkRect rect = SkRect::MakeXYWH(SkIntToScalar(SPACER), SkIntToScalar(SPACER),
79                                    SkIntToScalar(WIDTH-(2*SPACER)),
80                                    SkIntToScalar((HEIGHT-(2*SPACER)) / 7));
81 
82     const SkColorType colorTypes[] = {
83         kRGB_565_SkColorType, kN32_SkColorType
84     };
85 
86     const int layerAlpha[] = { 255, 255, 0 };
87     const SkCanvas::SaveLayerFlags flags[] = {
88         static_cast<SkCanvas::SaveLayerFlags>(SkCanvasPriv::kDontClipToLayer_SaveLayerFlag),
89         0,
90         static_cast<SkCanvas::SaveLayerFlags>(SkCanvasPriv::kDontClipToLayer_SaveLayerFlag),
91     };
92     REPORTER_ASSERT(reporter, sizeof(layerAlpha) == sizeof(flags));
93 
94     bool (*drawFn)(SkCanvasState* state, float l, float t,
95                    float r, float b, int32_t s);
96 
97     OpenLibResult openLibResult(reporter);
98     if (openLibResult.handle() != nullptr) {
99         *(void**) (&drawFn) = dlsym(openLibResult.handle(),
100                                     "complex_layers_draw_from_canvas_state");
101     } else {
102         drawFn = complex_layers_draw_from_canvas_state;
103     }
104 
105     REPORTER_ASSERT(reporter, drawFn);
106     if (!drawFn) {
107         return;
108     }
109 
110     for (size_t i = 0; i < SK_ARRAY_COUNT(colorTypes); ++i) {
111         SkBitmap bitmaps[2];
112         for (int j = 0; j < 2; ++j) {
113             bitmaps[j].allocPixels(SkImageInfo::Make(WIDTH, HEIGHT,
114                                                      colorTypes[i],
115                                                      kPremul_SkAlphaType));
116 
117             SkCanvas canvas(bitmaps[j]);
118 
119             canvas.drawColor(SK_ColorRED);
120 
121             for (size_t k = 0; k < SK_ARRAY_COUNT(layerAlpha); ++k) {
122                 SkTLazy<SkPaint> paint;
123                 if (layerAlpha[k] != 0xFF) {
124                     paint.init()->setAlpha(layerAlpha[k]);
125                 }
126 
127                 // draw a rect within the layer's bounds and again outside the layer's bounds
128                 canvas.saveLayer(SkCanvas::SaveLayerRec(&rect, paint.getMaybeNull(), flags[k]));
129 
130                 if (j) {
131                     // Capture from the first Skia.
132                     SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
133                     REPORTER_ASSERT(reporter, state);
134 
135                     // And draw to it in the second Skia.
136                     bool success = complex_layers_draw_from_canvas_state(state,
137                             rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, SPACER);
138                     REPORTER_ASSERT(reporter, success);
139 
140                     // And release it in the *first* Skia.
141                     SkCanvasStateUtils::ReleaseCanvasState(state);
142                 } else {
143                     // Draw in the first Skia.
144                     complex_layers_draw(&canvas, rect.fLeft, rect.fTop,
145                                         rect.fRight, rect.fBottom, SPACER);
146                 }
147 
148                 canvas.restore();
149 
150                 // translate the canvas for the next iteration
151                 canvas.translate(0, 2*(rect.height() + SPACER));
152             }
153         }
154 
155         // now we memcmp the two bitmaps
156         REPORTER_ASSERT(reporter, bitmaps[0].computeByteSize() == bitmaps[1].computeByteSize());
157         REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(),
158                                           bitmaps[1].getPixels(),
159                                           bitmaps[0].computeByteSize()));
160     }
161 }
162 #endif
163 
164 ////////////////////////////////////////////////////////////////////////////////
165 
166 #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
DEF_TEST(CanvasState_test_complex_clips,reporter)167 DEF_TEST(CanvasState_test_complex_clips, reporter) {
168     const int WIDTH = 400;
169     const int HEIGHT = 400;
170     const int SPACER = 10;
171 
172     SkIRect layerRect = SkIRect::MakeWH(WIDTH, HEIGHT / 4);
173     layerRect.inset(2*SPACER, 2*SPACER);
174 
175     SkIRect clipRect = layerRect;
176     clipRect.fRight = clipRect.fLeft + (clipRect.width() / 2) - (2*SPACER);
177     clipRect.outset(SPACER, SPACER);
178 
179     SkIRect regionBounds = clipRect;
180     regionBounds.offset(clipRect.width() + (2*SPACER), 0);
181 
182     SkIRect regionInterior = regionBounds;
183     regionInterior.inset(SPACER*3, SPACER*3);
184 
185     SkRegion clipRegion;
186     clipRegion.setRect(regionBounds);
187     clipRegion.op(regionInterior, SkRegion::kDifference_Op);
188 
189 
190     const SkRegion::Op clipOps[] = { SkRegion::kIntersect_Op,
191                                      SkRegion::kIntersect_Op,
192                                      SkRegion::kReplace_Op,
193     };
194     const SkCanvas::SaveLayerFlags flags[] = {
195         static_cast<SkCanvas::SaveLayerFlags>(SkCanvasPriv::kDontClipToLayer_SaveLayerFlag),
196         0,
197         static_cast<SkCanvas::SaveLayerFlags>(SkCanvasPriv::kDontClipToLayer_SaveLayerFlag),
198     };
199     REPORTER_ASSERT(reporter, sizeof(clipOps) == sizeof(flags));
200 
201     bool (*drawFn)(SkCanvasState* state, int32_t l, int32_t t,
202                    int32_t r, int32_t b, int32_t clipOp,
203                    int32_t regionRects, int32_t* rectCoords);
204 
205     OpenLibResult openLibResult(reporter);
206     if (openLibResult.handle() != nullptr) {
207         *(void**) (&drawFn) = dlsym(openLibResult.handle(),
208                                     "complex_clips_draw_from_canvas_state");
209     } else {
210         drawFn = complex_clips_draw_from_canvas_state;
211     }
212 
213     REPORTER_ASSERT(reporter, drawFn);
214     if (!drawFn) {
215         return;
216     }
217 
218     SkBitmap bitmaps[2];
219     for (int i = 0; i < 2; ++i) {
220         bitmaps[i].allocN32Pixels(WIDTH, HEIGHT);
221 
222         SkCanvas canvas(bitmaps[i]);
223 
224         canvas.drawColor(SK_ColorRED);
225 
226         SkRegion localRegion = clipRegion;
227 
228         SkPaint paint;
229         paint.setAlpha(128);
230         for (size_t j = 0; j < SK_ARRAY_COUNT(flags); ++j) {
231             SkRect layerBounds = SkRect::Make(layerRect);
232             canvas.saveLayer(SkCanvas::SaveLayerRec(&layerBounds, &paint, flags[j]));
233 
234             if (i) {
235                 SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
236                 REPORTER_ASSERT(reporter, state);
237 
238                 SkRegion::Iterator iter(localRegion);
239                 SkTDArray<int32_t> rectCoords;
240                 for (; !iter.done(); iter.next()) {
241                     const SkIRect& rect = iter.rect();
242                     *rectCoords.append() = rect.fLeft;
243                     *rectCoords.append() = rect.fTop;
244                     *rectCoords.append() = rect.fRight;
245                     *rectCoords.append() = rect.fBottom;
246                 }
247                 bool success = drawFn(state, clipRect.fLeft, clipRect.fTop,
248                                       clipRect.fRight, clipRect.fBottom, clipOps[j],
249                                       rectCoords.count() / 4, rectCoords.begin());
250                 REPORTER_ASSERT(reporter, success);
251 
252                 SkCanvasStateUtils::ReleaseCanvasState(state);
253             } else {
254                 complex_clips_draw(&canvas, clipRect.fLeft, clipRect.fTop,
255                                    clipRect.fRight, clipRect.fBottom, clipOps[j],
256                                    localRegion);
257             }
258 
259             canvas.restore();
260 
261             // translate the canvas and region for the next iteration
262             canvas.translate(0, SkIntToScalar(2*(layerRect.height() + (SPACER))));
263             localRegion.translate(0, 2*(layerRect.height() + SPACER));
264         }
265     }
266 
267     // now we memcmp the two bitmaps
268     REPORTER_ASSERT(reporter, bitmaps[0].computeByteSize() == bitmaps[1].computeByteSize());
269     REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(),
270                                       bitmaps[1].getPixels(),
271                                       bitmaps[0].computeByteSize()));
272 }
273 #endif
274 
275 ////////////////////////////////////////////////////////////////////////////////
276 
DEF_TEST(CanvasState_test_soft_clips,reporter)277 DEF_TEST(CanvasState_test_soft_clips, reporter) {
278     SkBitmap bitmap;
279     bitmap.allocN32Pixels(10, 10);
280     SkCanvas canvas(bitmap);
281 
282     SkRRect roundRect;
283     roundRect.setOval(SkRect::MakeWH(5, 5));
284 
285     canvas.clipRRect(roundRect, kIntersect_SkClipOp, true);
286 
287     SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
288     REPORTER_ASSERT(reporter, !state);
289 }
290 
DEF_TEST(CanvasState_test_saveLayer_clip,reporter)291 DEF_TEST(CanvasState_test_saveLayer_clip, reporter) {
292 #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
293     static_assert(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag ==
294                   SkCanvasPriv::kDontClipToLayer_SaveLayerFlag, "");
295 #endif
296     const int WIDTH = 100;
297     const int HEIGHT = 100;
298     const int LAYER_WIDTH = 50;
299     const int LAYER_HEIGHT = 50;
300 
301     SkBitmap bitmap;
302     bitmap.allocN32Pixels(WIDTH, HEIGHT);
303     SkCanvas canvas(bitmap);
304 
305     SkRect bounds = SkRect::MakeWH(SkIntToScalar(LAYER_WIDTH), SkIntToScalar(LAYER_HEIGHT));
306     canvas.clipRect(SkRect::MakeWH(SkIntToScalar(WIDTH), SkIntToScalar(HEIGHT)));
307 
308     SkIRect devClip;
309     // Check that saveLayer without the kClipToLayer_SaveFlag leaves the clip unchanged.
310     canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds, nullptr,
311             (SkCanvas::SaveLayerFlags) SkCanvasPriv::kDontClipToLayer_SaveLayerFlag));
312     devClip = canvas.getDeviceClipBounds();
313     REPORTER_ASSERT(reporter, canvas.isClipRect());
314     REPORTER_ASSERT(reporter, devClip.width() == WIDTH);
315     REPORTER_ASSERT(reporter, devClip.height() == HEIGHT);
316     canvas.restore();
317 
318     // Check that saveLayer with the kClipToLayer_SaveFlag sets the clip
319     // stack to the layer bounds.
320     canvas.saveLayer(&bounds, nullptr);
321     devClip = canvas.getDeviceClipBounds();
322     REPORTER_ASSERT(reporter, canvas.isClipRect());
323     REPORTER_ASSERT(reporter, devClip.width() == LAYER_WIDTH);
324     REPORTER_ASSERT(reporter, devClip.height() == LAYER_HEIGHT);
325     canvas.restore();
326 }
327