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