/* Hit a few SkPicture::Analysis cases not handled elsewhere. */ static void test_analysis(skiatest::Reporter* reporter) { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(100, 100); { canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ()); } sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps()); canvas = recorder.beginRecording(100, 100); { SkPaint paint; // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here. SkBitmap bitmap; bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2)); bitmap.eraseColor(SK_ColorBLUE); *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN; paint.setShader(SkShader::MakeBitmapShader(bitmap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)); REPORTER_ASSERT(reporter, paint.getShader()->isABitmap()); canvas->drawRect(SkRect::MakeWH(10, 10), paint); } REPORTER_ASSERT(reporter, recorder.finishRecordingAsPicture()->willPlayBackBitmaps()); }
// Test the kReturnNullForEmpty_FinishFlag option when recording // DEF_TEST(Picture_RecordEmpty, r) { const SkRect cull = SkRect::MakeWH(100, 100); CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops }; for (auto proc : procs) { { SkPictureRecorder rec; proc(rec.beginRecording(cull)); sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0); REPORTER_ASSERT(r, pic.get()); REPORTER_ASSERT(r, pic->approximateOpCount() == 0); } { SkPictureRecorder rec; proc(rec.beginRecording(cull)); sk_sp<SkPicture> pic = rec.finishRecordingAsPicture( SkPictureRecorder::kReturnNullForEmpty_FinishFlag); REPORTER_ASSERT(r, !pic.get()); } { SkPictureRecorder rec; proc(rec.beginRecording(cull)); sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0); REPORTER_ASSERT(r, dr.get()); } { SkPictureRecorder rec; proc(rec.beginRecording(cull)); sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable( SkPictureRecorder::kReturnNullForEmpty_FinishFlag); REPORTER_ASSERT(r, !dr.get()); } } }
TEST_F(DeferredImageDecoderTest, drawIntoSkPictureProgressive) { RefPtr<SharedBuffer> partialData = SharedBuffer::create(m_data->data(), m_data->size() - 10); // Received only half the file. m_lazyDecoder->setData(partialData, false); sk_sp<SkImage> image = m_lazyDecoder->createFrameAtIndex(0); ASSERT_TRUE(image); SkPictureRecorder recorder; SkCanvas* tempCanvas = recorder.beginRecording(100, 100, 0, 0); tempCanvas->drawImage(image.get(), 0, 0); m_surface->getCanvas()->drawPicture(recorder.finishRecordingAsPicture()); // Fully received the file and draw the SkPicture again. m_lazyDecoder->setData(m_data, true); image = m_lazyDecoder->createFrameAtIndex(0); ASSERT_TRUE(image); tempCanvas = recorder.beginRecording(100, 100, 0, 0); tempCanvas->drawImage(image.get(), 0, 0); m_surface->getCanvas()->drawPicture(recorder.finishRecordingAsPicture()); SkBitmap canvasBitmap; canvasBitmap.allocN32Pixels(100, 100); ASSERT_TRUE(m_surface->getCanvas()->readPixels(&canvasBitmap, 0, 0)); SkAutoLockPixels autoLock(canvasBitmap); EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255), canvasBitmap.getColor(0, 0)); }
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 test_unbalanced_save_restores(skiatest::Reporter* reporter) { SkCanvas testCanvas(100, 100); set_canvas_to_save_count_4(&testCanvas); REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); SkPaint paint; SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); SkPictureRecorder recorder; { // Create picture with 2 unbalanced saves SkCanvas* canvas = recorder.beginRecording(100, 100); canvas->save(); canvas->translate(10, 10); canvas->drawRect(rect, paint); canvas->save(); canvas->translate(10, 10); canvas->drawRect(rect, paint); sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture()); testCanvas.drawPicture(extraSavePicture); REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); } set_canvas_to_save_count_4(&testCanvas); { // Create picture with 2 unbalanced restores SkCanvas* canvas = recorder.beginRecording(100, 100); canvas->save(); canvas->translate(10, 10); canvas->drawRect(rect, paint); canvas->save(); canvas->translate(10, 10); canvas->drawRect(rect, paint); canvas->restore(); canvas->restore(); canvas->restore(); canvas->restore(); sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture()); testCanvas.drawPicture(extraRestorePicture); REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); } set_canvas_to_save_count_4(&testCanvas); { SkCanvas* canvas = recorder.beginRecording(100, 100); canvas->translate(10, 10); canvas->drawRect(rect, paint); sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture()); testCanvas.drawPicture(noSavePicture); REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); } }
static void test_peephole() { SkRandom rand; SkPictureRecorder recorder; for (int j = 0; j < 100; j++) { SkRandom rand2(rand); // remember the seed SkCanvas* canvas = recorder.beginRecording(100, 100); for (int i = 0; i < 1000; ++i) { rand_op(canvas, rand); } sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); rand = rand2; } { SkCanvas* canvas = recorder.beginRecording(100, 100); SkRect rect = SkRect::MakeWH(50, 50); for (int i = 0; i < 100; ++i) { canvas->save(); } while (canvas->getSaveCount() > 1) { canvas->clipRect(rect); canvas->restore(); } sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); } }
int tool_main(int argc, char** argv) { SkCommandLineFlags::Parse(argc, argv); for (int i = 0; i < FLAGS_skps.count(); i++) { if (SkCommandLineFlags::ShouldSkip(FLAGS_match, FLAGS_skps[i])) { continue; } SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(FLAGS_skps[i])); if (!stream) { SkDebugf("Could not read %s.\n", FLAGS_skps[i]); return 1; } sk_sp<SkPicture> src(SkPicture::MakeFromStream(stream)); if (!src) { SkDebugf("Could not read %s as an SkPicture.\n", FLAGS_skps[i]); return 1; } if (FLAGS_defer) { SkPictureRecorder recorder; SkDeferredCanvas deferred(recorder.beginRecording(src->cullRect())); src->playback(&deferred); src = recorder.finishRecordingAsPicture(); } const int w = SkScalarCeilToInt(src->cullRect().width()); const int h = SkScalarCeilToInt(src->cullRect().height()); SkRecord record; SkRecorder canvas(&record, w, h); src->playback(&canvas); if (FLAGS_optimize) { SkRecordOptimize(&record); } if (FLAGS_optimize2) { SkRecordOptimize2(&record); } dump(FLAGS_skps[i], w, h, record); if (FLAGS_write.count() > 0) { SkPictureRecorder r; SkRecordDraw(record, r.beginRecording(SkRect::MakeIWH(w, h)), nullptr, nullptr, 0, nullptr, nullptr); sk_sp<SkPicture> dst(r.finishRecordingAsPicture()); SkFILEWStream ostream(FLAGS_write[0]); dst->serialize(&ostream); } } return 0; }
// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. static void test_serializing_empty_picture() { SkPictureRecorder recorder; recorder.beginRecording(0, 0); sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); SkDynamicMemoryWStream stream; picture->serialize(&stream); }
// Verify that associated bitmap cache entries are purged on SkImage destruction. DEF_TEST(BitmapCache_discarded_image, reporter) { // Cache entries associated with SkImages fall into two categories: // // 1) generated image bitmaps (managed by the image cacherator) // 2) scaled/resampled bitmaps (cached when HQ filters are used) // // To exercise the first cache type, we use generated/picture-backed SkImages. // To exercise the latter, we draw scaled bitmap images using HQ filters. const SkMatrix xforms[] = { SkMatrix::MakeScale(1, 1), SkMatrix::MakeScale(1.7f, 0.5f), }; for (size_t i = 0; i < SK_ARRAY_COUNT(xforms); ++i) { test_discarded_image(reporter, xforms[i], []() { auto surface(SkSurface::MakeRasterN32Premul(10, 10)); surface->getCanvas()->clear(SK_ColorCYAN); return surface->makeImageSnapshot(); }); test_discarded_image(reporter, xforms[i], []() { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->clear(SK_ColorCYAN); return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), SkISize::Make(10, 10), nullptr, nullptr, SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()); }); } }
TEST_F(DeferredImageDecoderTest, decodeOnOtherThread) { m_lazyDecoder->setData(m_data, true); sk_sp<SkImage> image = m_lazyDecoder->createFrameAtIndex(0); ASSERT_TRUE(image); EXPECT_EQ(1, image->width()); EXPECT_EQ(1, image->height()); SkPictureRecorder recorder; SkCanvas* tempCanvas = recorder.beginRecording(100, 100, 0, 0); tempCanvas->drawImage(image.get(), 0, 0); sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); EXPECT_EQ(0, m_decodeRequestCount); // Create a thread to rasterize SkPicture. std::unique_ptr<WebThread> thread = WTF::wrapUnique(Platform::current()->createThread("RasterThread")); thread->getWebTaskRunner()->postTask( BLINK_FROM_HERE, crossThreadBind(&rasterizeMain, crossThreadUnretained(m_surface->getCanvas()), crossThreadUnretained(picture.get()))); thread.reset(); EXPECT_EQ(0, m_decodeRequestCount); SkBitmap canvasBitmap; canvasBitmap.allocN32Pixels(100, 100); ASSERT_TRUE(m_surface->getCanvas()->readPixels(&canvasBitmap, 0, 0)); SkAutoLockPixels autoLock(canvasBitmap); EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255), canvasBitmap.getColor(0, 0)); }
// Test that the SkPictureShader cache is purged on shader deletion. DEF_TEST(PictureShader_caching, reporter) { auto makePicture = [] () { SkPictureRecorder recorder; recorder.beginRecording(100, 100)->drawColor(SK_ColorGREEN); return recorder.finishRecordingAsPicture(); }; sk_sp<SkPicture> picture = makePicture(); REPORTER_ASSERT(reporter, picture->unique()); sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100); { SkPaint paint; paint.setShader(picture->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat)); surface->getCanvas()->drawPaint(paint); // We should have about 3 refs by now: local + shader + shader cache. REPORTER_ASSERT(reporter, !picture->unique()); } // Draw another picture shader to have a chance to purge. { SkPaint paint; paint.setShader(makePicture()->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat)); surface->getCanvas()->drawPaint(paint); } // All but the local ref should be gone now. REPORTER_ASSERT(reporter, picture->unique()); }
PassRefPtr<PictureSnapshot> PictureSnapshot::load(const Vector<RefPtr<TilePictureStream>>& tiles) { ASSERT(!tiles.isEmpty()); Vector<sk_sp<SkPicture>> pictures; pictures.reserveCapacity(tiles.size()); FloatRect unionRect; for (const auto& tileStream : tiles) { SkMemoryStream stream(tileStream->data.begin(), tileStream->data.size()); sk_sp<SkPicture> picture = SkPicture::MakeFromStream(&stream, decodeBitmap); if (!picture) return nullptr; FloatRect cullRect(picture->cullRect()); cullRect.moveBy(tileStream->layerOffset); unionRect.unite(cullRect); pictures.append(std::move(picture)); } if (tiles.size() == 1) return adoptRef(new PictureSnapshot(fromSkSp(std::move(pictures[0])))); SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(unionRect.width(), unionRect.height(), 0, 0); for (size_t i = 0; i < pictures.size(); ++i) { canvas->save(); canvas->translate(tiles[i]->layerOffset.x() - unionRect.x(), tiles[i]->layerOffset.y() - unionRect.y()); pictures[i]->playback(canvas, 0); canvas->restore(); } return adoptRef(new PictureSnapshot(fromSkSp(recorder.finishRecordingAsPicture()))); }
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(); }
PictureCentricBench::PictureCentricBench(const char* name, const SkPicture* pic) : fName(name) { // Flatten the source picture in case it's trivially nested (useless for timing). SkPictureRecorder rec; pic->playback(rec.beginRecording(pic->cullRect(), nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag)); fSrc = rec.finishRecordingAsPicture(); }
DEF_TEST(Picture_BitmapLeak, r) { SkBitmap mut, immut; mut.allocN32Pixels(300, 200); immut.allocN32Pixels(300, 200); immut.setImmutable(); SkASSERT(!mut.isImmutable()); SkASSERT(immut.isImmutable()); // No one can hold a ref on our pixels yet. REPORTER_ASSERT(r, mut.pixelRef()->unique()); REPORTER_ASSERT(r, immut.pixelRef()->unique()); sk_sp<SkPicture> pic; { // we want the recorder to go out of scope before our subsequent checks, so we // place it inside local braces. SkPictureRecorder rec; SkCanvas* canvas = rec.beginRecording(1920, 1200); canvas->drawBitmap(mut, 0, 0); canvas->drawBitmap(immut, 800, 600); pic = rec.finishRecordingAsPicture(); } // The picture shares the immutable pixels but copies the mutable ones. REPORTER_ASSERT(r, mut.pixelRef()->unique()); REPORTER_ASSERT(r, !immut.pixelRef()->unique()); // When the picture goes away, it's just our bitmaps holding the refs. pic = nullptr; REPORTER_ASSERT(r, mut.pixelRef()->unique()); REPORTER_ASSERT(r, immut.pixelRef()->unique()); }
void onOnceBeforeDraw() override { const SkRect rect = SkRect::MakeWH(kPictureWidth, kPictureHeight); SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(rect); draw_vector_logo(canvas, rect); fPicture = recorder.finishRecordingAsPicture(); }
void GMSampleView::onDrawContent(SkCanvas* canvas) { SkPictureRecorder recorder; SkCanvas* origCanvas = canvas; if (false) { SkISize size = fGM->getISize(); canvas = recorder.beginRecording(SkRect::MakeIWH(size.width(), size.height())); } { SkAutoCanvasRestore acr(canvas, fShowSize); fGM->drawContent(canvas); } if (origCanvas != canvas) { sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture(); if (false) { pic = round_trip_serialize(pic.get()); } origCanvas->drawPicture(pic); canvas = origCanvas; } if (fShowSize) { SkISize size = fGM->getISize(); SkRect r = SkRect::MakeWH(SkIntToScalar(size.width()), SkIntToScalar(size.height())); SkPaint paint; paint.setColor(0x40FF8833); canvas->drawRect(r, paint); } }
static void serialize_and_compare_typeface(sk_sp<SkTypeface> typeface, const char* text, skiatest::Reporter* reporter) { // Create a paint with the typeface. SkPaint paint; paint.setColor(SK_ColorGRAY); paint.setTextSize(SkIntToScalar(30)); paint.setTypeface(std::move(typeface)); // Paint some text. SkPictureRecorder recorder; SkIRect canvasRect = SkIRect::MakeWH(kBitmapSize, kBitmapSize); SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(canvasRect.width()), SkIntToScalar(canvasRect.height()), nullptr, 0); canvas->drawColor(SK_ColorWHITE); canvas->drawText(text, 2, 24, 32, paint); sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); // Serlialize picture and create its clone from stream. SkDynamicMemoryWStream stream; picture->serialize(&stream); std::unique_ptr<SkStream> inputStream(stream.detachAsStream()); sk_sp<SkPicture> loadedPicture(SkPicture::MakeFromStream(inputStream.get())); // Draw both original and clone picture and compare bitmaps -- they should be identical. SkBitmap origBitmap = draw_picture(*picture); SkBitmap destBitmap = draw_picture(*loadedPicture); compare_bitmaps(reporter, origBitmap, destBitmap); }
/* * Test the 3 annotation types by recording them into a picture, serializing, and then playing * them back into another canvas. */ DEF_TEST(Annotations, reporter) { SkPictureRecorder recorder; SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeWH(100, 100)); const char* str0 = "rect-with-url"; const SkRect r0 = SkRect::MakeWH(10, 10); sk_sp<SkData> d0(SkData::MakeWithCString(str0)); SkAnnotateRectWithURL(recordingCanvas, r0, d0.get()); const char* str1 = "named-destination"; const SkRect r1 = SkRect::MakeXYWH(5, 5, 0, 0); // collapsed to a point sk_sp<SkData> d1(SkData::MakeWithCString(str1)); SkAnnotateNamedDestination(recordingCanvas, {r1.x(), r1.y()}, d1.get()); const char* str2 = "link-to-destination"; const SkRect r2 = SkRect::MakeXYWH(20, 20, 5, 6); sk_sp<SkData> d2(SkData::MakeWithCString(str2)); SkAnnotateLinkToDestination(recordingCanvas, r2, d2.get()); const AnnotationRec recs[] = { { r0, SkAnnotationKeys::URL_Key(), std::move(d0) }, { r1, SkAnnotationKeys::Define_Named_Dest_Key(), std::move(d1) }, { r2, SkAnnotationKeys::Link_Named_Dest_Key(), std::move(d2) }, }; sk_sp<SkPicture> pict0(recorder.finishRecordingAsPicture()); sk_sp<SkPicture> pict1(copy_picture_via_serialization(pict0.get())); TestAnnotationCanvas canvas(reporter, recs, SK_ARRAY_COUNT(recs)); canvas.drawPicture(pict1); }
// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to // inner savelayer. We know that alpha folding happens to inner savelayer, so add detector there. static void draw_svg_opacity_and_filter_layer_sequence(SkCanvas* canvas, SkColor shapeColor, InstallDetectorFunc installDetector) { SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize))); sk_sp<SkPicture> shape; { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kTestRectSize + 2), SkIntToScalar(kTestRectSize + 2)); SkPaint shapePaint; shapePaint.setColor(shapeColor); canvas->drawRect(targetRect, shapePaint); shape = recorder.finishRecordingAsPicture(); } SkPaint layerPaint; layerPaint.setColor(SkColorSetARGB(130, 0, 0, 0)); canvas->saveLayer(&targetRect, &layerPaint); canvas->save(); canvas->clipRect(targetRect); SkPaint drawPaint; drawPaint.setImageFilter(SkPictureImageFilter::Create(shape.get()))->unref(); installDetector(&drawPaint); canvas->saveLayer(&targetRect, &drawPaint); canvas->restore(); canvas->restore(); canvas->restore(); }
void onDraw(int loops, SkCanvas*) override { SkLiteRecorder lite; SkPictureRecorder rec; for (int i = 0; i < loops; i++) { SkRect bounds{0,0, 2000,3000}; sk_sp<SkLiteDL> liteDL; SkCanvas* canvas; if (kLite) { liteDL = SkLiteDL::New(bounds); lite.reset(liteDL.get()); canvas = &lite; } else { rec.beginRecording(bounds); canvas = rec.getRecordingCanvas(); } for (int i = 0; i < kDraws; i++) { canvas->drawRect({10,10, 1000, 1000}, SkPaint{}); } if (!kLite) { (void)rec.finishRecordingAsPicture(); } } }
static sk_sp<SkPicture> make_sub_picture(const SkPicture* tri) { SkPictureRecorder recorder; SkRTreeFactory bbhFactory; SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth), SkIntToScalar(kPicHeight), &bbhFactory); canvas->scale(1.0f/2.0f, 1.0f/2.0f); canvas->save(); canvas->translate(SkScalarHalf(kTriSide), 0); canvas->drawPicture(tri); canvas->restore(); canvas->save(); canvas->translate(SkIntToScalar(kTriSide), 1.5f * kTriSide / kRoot3); canvas->drawPicture(tri); canvas->restore(); canvas->save(); canvas->translate(0, 1.5f * kTriSide / kRoot3); canvas->drawPicture(tri); canvas->restore(); return recorder.finishRecordingAsPicture(); }
// Create a Sierpinkski-like picture that starts with a top row with a picture // that just contains a triangle. Subsequent rows take the prior row's picture, // shrinks it and replicates it 3 times then draws and appropriate number of // copies of it. static sk_sp<SkPicture> make_sierpinski_picture() { sk_sp<SkPicture> pic(make_tri_picture()); SkPictureRecorder recorder; SkRTreeFactory bbhFactory; SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth), SkIntToScalar(kPicHeight), &bbhFactory); constexpr int kNumLevels = 4; for (int i = 0; i < kNumLevels; ++i) { canvas->save(); canvas->translate(kPicWidth/2 - (i+1) * (kTriSide/2.0f), 0.0f); for (int j = 0; j < i+1; ++j) { canvas->drawPicture(pic); canvas->translate(SkIntToScalar(kTriSide), 0); } canvas->restore(); pic = make_sub_picture(pic.get()); canvas->translate(0, 1.5f * kTriSide / kRoot3); } return recorder.finishRecordingAsPicture(); }
// Make sure GrRecordReplaceDraw balances unbalanced saves DEF_TEST(RecordReplaceDraw_Unbalanced, r) { sk_sp<SkPicture> pic; { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight)); // We won't balance this, but GrRecordReplaceDraw will for us. canvas->save(); canvas->scale(2, 2); pic = recorder.finishRecordingAsPicture(); // we may have optimized everything away. If so, just return if (pic->approximateOpCount() == 0) { return; } } SkRecord rerecord; SkRecorder canvas(&rerecord, kWidth, kHeight); GrRecordReplaceDraw(pic.get(), &canvas, nullptr, SkMatrix::I(), nullptr/*callback*/); // ensure rerecord is balanced (in this case by checking that the count is odd) REPORTER_ASSERT(r, (rerecord.count() & 1) == 1); }
static sk_sp<SkPicture> make_tri_picture() { SkPath tri = make_tri_path(SkScalarHalf(kTriSide), 0); SkPaint fill; fill.setStyle(SkPaint::kFill_Style); fill.setColor(sk_tool_utils::color_to_565(SK_ColorLTGRAY)); SkPaint stroke; stroke.setStyle(SkPaint::kStroke_Style); stroke.setStrokeWidth(3); SkPictureRecorder recorder; SkRTreeFactory bbhFactory; SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth), SkIntToScalar(kPicHeight), &bbhFactory); SkRect r = tri.getBounds(); r.outset(2.0f, 2.0f); // outset for stroke canvas->clipRect(r); // The saveLayer/restore block is to exercise layer hoisting canvas->saveLayer(nullptr, nullptr); canvas->drawPath(tri, fill); canvas->drawPath(tri, stroke); canvas->restore(); return recorder.finishRecordingAsPicture(); }
// Make sure the abort callback works DEF_TEST(RecordReplaceDraw_Abort, r) { sk_sp<SkPicture> pic; { // Record two commands. SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight)); canvas->drawRect(SkRect::MakeWH(SkIntToScalar(kWidth), SkIntToScalar(kHeight)), SkPaint()); canvas->clipRect(SkRect::MakeWH(SkIntToScalar(kWidth), SkIntToScalar(kHeight))); pic = recorder.finishRecordingAsPicture(); } SkRecord rerecord; SkRecorder canvas(&rerecord, kWidth, kHeight); JustOneDraw callback; GrRecordReplaceDraw(pic.get(), &canvas, nullptr, SkMatrix::I(), &callback); switch (rerecord.count()) { case 3: assert_type<SkRecords::Save>(r, rerecord, 0); assert_type<SkRecords::DrawRect>(r, rerecord, 1); assert_type<SkRecords::Restore>(r, rerecord, 2); break; case 1: assert_type<SkRecords::DrawRect>(r, rerecord, 0); break; default: REPORTER_ASSERT(r, false); } }
static sk_sp<SkImage> make_picture_image() { SkPictureRecorder recorder; draw_contents(recorder.beginRecording(SkRect::MakeIWH(kWidth, kHeight))); return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), SkISize::Make(kWidth, kHeight), nullptr, nullptr, SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()); }
DEF_TEST(PictureGpuAnalyzer, r) { SkPictureRecorder recorder; { SkCanvas* canvas = recorder.beginRecording(10, 10); 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); } } sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture()); SkPictureGpuAnalyzer analyzer; REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); analyzer.analyzePicture(vetoPicture.get()); REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); analyzer.reset(); REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); recorder.beginRecording(10, 10)->drawPicture(vetoPicture); sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture()); analyzer.analyzePicture(nestedVetoPicture.get()); REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); analyzer.reset(); const SkPath convexClip = make_convex_path(); const SkPath concaveClip = make_concave_path(); for (int i = 0; i < 50; ++i) { analyzer.analyzeClipPath(convexClip, SkRegion::kIntersect_Op, false); analyzer.analyzeClipPath(convexClip, SkRegion::kIntersect_Op, true); analyzer.analyzeClipPath(concaveClip, SkRegion::kIntersect_Op, false); } REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); for (int i = 0; i < 50; ++i) { analyzer.analyzeClipPath(concaveClip, SkRegion::kIntersect_Op, true); } REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); }
void draw(SkCanvas* canvas) { SkPictureRecorder recorder; recorder.beginRecording({0, 0, 0, 0}); sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); SkDebugf("empty picture id = %d\n", picture->uniqueID()); sk_sp<SkPicture> placeholder = SkPicture::MakePlaceholder({0, 0, 0, 0}); SkDebugf("placeholder id = %d\n", placeholder->uniqueID()); }
// Ensure that deleting an empty SkPicture does not assert. Asserts only fire // in debug mode, so only run in debug mode. static void test_deleting_empty_picture() { SkPictureRecorder recorder; // Creates an SkPictureRecord recorder.beginRecording(0, 0); // Turns that into an SkPicture sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); // Ceates a new SkPictureRecord recorder.beginRecording(0, 0); }