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