void onDraw(SkCanvas* canvas) override { SkPictureRecorder recorder; SkCanvas* rec = recorder.beginRecording(1200, 900, nullptr, 0); SkPath p; SkRect r = { SkIntToScalar(100), SkIntToScalar(200), SkIntToScalar(400), SkIntToScalar(700) }; p.addRoundRect(r, SkIntToScalar(50), SkIntToScalar(50)); rec->clipPath(p, SkRegion::kIntersect_Op, true); rec->translate(SkIntToScalar(250), SkIntToScalar(250)); rec->clipPath(p, SkRegion::kIntersect_Op, true); rec->drawColor(0xffff0000); sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); canvas->setAllowSimplifyClip(true); canvas->save(); canvas->drawPicture(pict); canvas->restore(); canvas->setAllowSimplifyClip(false); canvas->save(); canvas->translate(SkIntToScalar(1200 / 2), 0); canvas->drawPicture(pict); canvas->restore(); }
virtual void onDraw(SkCanvas* canvas) { constexpr SkScalar kOffset = 35000.0f; constexpr SkScalar kExtents = 1000.0f; SkPictureRecorder recorder; // We record a picture of huge vertical extents in which we clear the canvas to red, create // a 'extents' by 'extents' round rect clip at a vertical offset of 'offset', then draw // green into that. SkCanvas* rec = recorder.beginRecording(kExtents, kOffset + kExtents, nullptr, 0); rec->drawColor(SK_ColorRED); rec->save(); SkRect r = SkRect::MakeXYWH(-kExtents, kOffset - kExtents, 2 * kExtents, 2 * kExtents); SkPath p; p.addRoundRect(r, 5, 5); rec->clipPath(p, true); rec->drawColor(SK_ColorGREEN); rec->restore(); sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); // Next we play that picture into another picture of the same size. pict->playback(recorder.beginRecording(pict->cullRect().width(), pict->cullRect().height(), nullptr, 0)); sk_sp<SkPicture> pict2(recorder.finishRecordingAsPicture()); // Finally we play the part of that second picture that should be green into the canvas. canvas->save(); canvas->translate(kExtents / 2, -(kOffset - kExtents / 2)); pict2->playback(canvas); canvas->restore(); // If the image is red, we erroneously decided the clipPath was empty and didn't record // the green drawColor, if it's green we're all good. }
static void fuzz_drawPath(Fuzz* fuzz) { SkPaint p; init_paint(fuzz, &p); sk_sp<SkSurface> surface; init_surface(fuzz, &surface); // TODO(kjlubick): put the ability to fuzz a path in shared file, with // other common things (e.g. rects, lines) uint8_t i, j; fuzz->nextRange(&i, 0, 10); // set i to number of operations to perform SkPath path; SkScalar a, b, c, d, e, f; for (int k = 0; k < i; ++k) { fuzz->nextRange(&j, 0, 5); // set j to choose operation to perform switch (j) { case 0: fuzz->next(&a, &b); path.moveTo(a, b); break; case 1: fuzz->next(&a, &b); path.lineTo(a, b); break; case 2: fuzz->next(&a, &b, &c, &d); path.quadTo(a, b, c, d); break; case 3: fuzz->next(&a, &b, &c, &d, &e); path.conicTo(a, b, c, d, e); break; case 4: fuzz->next(&a, &b, &c, &d, &e, &f); path.cubicTo(a, b, c, d, e, f); break; case 5: fuzz->next(&a, &b, &c, &d, &e); path.arcTo(a, b, c, d, e); break; } } path.close(); SkCanvas* cnv = surface->getCanvas(); cnv->drawPath(path, p); bool bl; fuzz->next(&bl); cnv->clipPath(path, kIntersect_SkClipOp, bl); }
virtual void onDraw(SkCanvas* canvas) { int offset = 35000; int extents = 1000; // We record a picture of huge vertical extents in which we clear the canvas to red, create // a 'extents' by 'extents' round rect clip at a vertical offset of 'offset', then draw // green into that. SkPicture pict; SkCanvas* rec = pict.beginRecording(100, offset + extents); rec->drawColor(0xffff0000); rec->save(); SkRect r = { SkIntToScalar(-extents), SkIntToScalar(offset - extents), SkIntToScalar(extents), SkIntToScalar(offset + extents) }; SkPath p; p.addRoundRect(r, 5, 5); rec->clipPath(p, SkRegion::kIntersect_Op, true); rec->drawColor(0xff00ff00); rec->restore(); pict.endRecording(); // Next we play that picture into another picture of the same size. SkPicture pict2; pict.draw(pict2.beginRecording(100, offset + extents)); pict2.endRecording(); // Finally we play the part of that second picture that should be green into the canvas. canvas->save(); canvas->translate(SkIntToScalar(extents / 2), SkIntToScalar(-(offset - extents / 2))); pict2.draw(canvas); canvas->restore(); // If the image is red, we erroneously decided the clipPath was empty and didn't record // the green drawColor, if it's green we're all good. }
static void _prepare_sized_pattern(mbe_t *mbe, mbe_pattern_t *ptn) { SkCanvas *canvas = mbe->canvas; SkPath path; co_aix x, y; co_aix reverse[6]; *mbe->saved_region = canvas->getTotalClip(); compute_reverse(ptn->matrix, reverse); x = 0; y = 0; matrix_trans_pos(reverse, &x, &y); path.moveTo(CO_AIX_2_SKSCALAR(x), CO_AIX_2_SKSCALAR(y)); x = 0; y = ptn->h; matrix_trans_pos(reverse, &x, &y); path.moveTo(CO_AIX_2_SKSCALAR(x), CO_AIX_2_SKSCALAR(y)); x = ptn->w; y = ptn->h; matrix_trans_pos(reverse, &x, &y); path.moveTo(CO_AIX_2_SKSCALAR(x), CO_AIX_2_SKSCALAR(y)); path.close(); canvas->clipPath(path, SkRegion::kIntersect_Op); }
static void test_clip_bound_opt(skiatest::Reporter* reporter) { // Test for crbug.com/229011 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), SkIntToScalar(2), SkIntToScalar(2)); SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), SkIntToScalar(1), SkIntToScalar(1)); SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), SkIntToScalar(1), SkIntToScalar(1)); SkPath invPath; invPath.addOval(rect1); invPath.setFillType(SkPath::kInverseEvenOdd_FillType); SkPath path; path.addOval(rect2); SkPath path2; path2.addOval(rect3); SkIRect clipBounds; SkPictureRecorder recorder; // Testing conservative-raster-clip that is enabled by PictureRecord { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->clipPath(invPath, SkRegion::kIntersect_Op); bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); REPORTER_ASSERT(reporter, true == nonEmpty); REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->clipPath(path, SkRegion::kIntersect_Op); canvas->clipPath(invPath, SkRegion::kIntersect_Op); bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); REPORTER_ASSERT(reporter, true == nonEmpty); REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->clipPath(path, SkRegion::kIntersect_Op); canvas->clipPath(invPath, SkRegion::kUnion_Op); bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); REPORTER_ASSERT(reporter, true == nonEmpty); REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->clipPath(path, SkRegion::kDifference_Op); bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); REPORTER_ASSERT(reporter, true == nonEmpty); REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->clipPath(path, SkRegion::kReverseDifference_Op); bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); // True clip is actually empty in this case, but the best // determination we can make using only bounds as input is that the // clip is included in the bounds of 'path'. REPORTER_ASSERT(reporter, true == nonEmpty); REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->clipPath(path, SkRegion::kIntersect_Op); canvas->clipPath(path2, SkRegion::kXOR_Op); bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); REPORTER_ASSERT(reporter, true == nonEmpty); REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); } }
static void test_gpu_veto(skiatest::Reporter* reporter) { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(100, 100); { SkPath path; path.moveTo(0, 0); path.lineTo(50, 50); SkScalar intervals[] = { 1.0f, 1.0f }; sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0)); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setPathEffect(dash); for (int i = 0; i < 50; ++i) { canvas->drawPath(path, paint); } } sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); // path effects currently render an SkPicture undesireable for GPU rendering const char *reason = nullptr; REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization(&reason)); REPORTER_ASSERT(reporter, reason); canvas = recorder.beginRecording(100, 100); { SkPath path; path.moveTo(0, 0); path.lineTo(0, 50); path.lineTo(25, 25); path.lineTo(50, 50); path.lineTo(50, 0); path.close(); REPORTER_ASSERT(reporter, !path.isConvex()); SkPaint paint; paint.setAntiAlias(true); for (int i = 0; i < 50; ++i) { canvas->drawPath(path, paint); } } picture = recorder.finishRecordingAsPicture(); // A lot of small AA concave paths should be fine for GPU rendering REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); canvas = recorder.beginRecording(100, 100); { SkPath path; path.moveTo(0, 0); path.lineTo(0, 100); path.lineTo(50, 50); path.lineTo(100, 100); path.lineTo(100, 0); path.close(); REPORTER_ASSERT(reporter, !path.isConvex()); SkPaint paint; paint.setAntiAlias(true); for (int i = 0; i < 50; ++i) { canvas->drawPath(path, paint); } } picture = recorder.finishRecordingAsPicture(); // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); canvas = recorder.beginRecording(100, 100); { SkPath path; path.moveTo(0, 0); path.lineTo(0, 50); path.lineTo(25, 25); path.lineTo(50, 50); path.lineTo(50, 0); path.close(); REPORTER_ASSERT(reporter, !path.isConvex()); SkPaint paint; paint.setAntiAlias(true); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(0); for (int i = 0; i < 50; ++i) { canvas->drawPath(path, paint); } } picture = recorder.finishRecordingAsPicture(); // hairline stroked AA concave paths are fine for GPU rendering REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); canvas = recorder.beginRecording(100, 100); { SkPaint paint; SkScalar intervals [] = { 10, 20 }; paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); SkPoint points [2] = { { 0, 0 }, { 100, 0 } }; for (int i = 0; i < 50; ++i) { canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint); } } picture = recorder.finishRecordingAsPicture(); // fast-path dashed effects are fine for GPU rendering ... REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); canvas = recorder.beginRecording(100, 100); { SkPaint paint; SkScalar intervals [] = { 10, 20 }; paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); for (int i = 0; i < 50; ++i) { canvas->drawRect(SkRect::MakeWH(10, 10), paint); } } picture = recorder.finishRecordingAsPicture(); // ... but only when applied to drawPoint() calls REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); canvas = recorder.beginRecording(100, 100); { const SkPath convexClip = make_convex_path(); const SkPath concaveClip = make_concave_path(); for (int i = 0; i < 50; ++i) { canvas->clipPath(convexClip); canvas->clipPath(concaveClip); canvas->clipPath(convexClip, SkRegion::kIntersect_Op, true); canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint()); } } picture = recorder.finishRecordingAsPicture(); // Convex clips and non-AA concave clips are fine on the GPU. REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); canvas = recorder.beginRecording(100, 100); { const SkPath concaveClip = make_concave_path(); for (int i = 0; i < 50; ++i) { canvas->clipPath(concaveClip, SkRegion::kIntersect_Op, true); canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint()); } } picture = recorder.finishRecordingAsPicture(); // ... but AA concave clips are not. REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); // Nest the previous picture inside a new one. canvas = recorder.beginRecording(100, 100); { canvas->drawPicture(picture); } picture = recorder.finishRecordingAsPicture(); REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); }