static sk_sp<SkImageFilter> make_image_filter(bool canBeNull) { sk_sp<SkImageFilter> filter; // Add a 1 in 3 chance to get a nullptr input if (canBeNull && (R(3) == 1)) { return filter; } enum { ALPHA_THRESHOLD, MERGE, COLOR, BLUR, MAGNIFIER, XFERMODE, OFFSET, MATRIX, MATRIX_CONVOLUTION, COMPOSE, DISTANT_LIGHT, POINT_LIGHT, SPOT_LIGHT, NOISE, DROP_SHADOW, MORPHOLOGY, BITMAP, DISPLACE, TILE, PICTURE, PAINT, NUM_FILTERS }; switch (R(NUM_FILTERS)) { case ALPHA_THRESHOLD: filter = SkAlphaThresholdFilter::Make(make_region(), make_scalar(), make_scalar(), make_image_filter()); break; case MERGE: filter = SkMergeImageFilter::Make(make_image_filter(), make_image_filter()); break; case COLOR: { sk_sp<SkColorFilter> cf(make_color_filter()); filter = cf ? SkColorFilterImageFilter::Make(std::move(cf), make_image_filter()) : nullptr; break; } case BLUR: filter = SkBlurImageFilter::Make(make_scalar(true), make_scalar(true), make_image_filter()); break; case MAGNIFIER: filter = SkMagnifierImageFilter::Make(make_rect(), make_scalar(true), make_image_filter()); break; case XFERMODE: filter = SkXfermodeImageFilter::Make(make_xfermode(), make_image_filter(), make_image_filter(), nullptr); break; case OFFSET: filter = SkOffsetImageFilter::Make(make_scalar(), make_scalar(), make_image_filter()); break; case MATRIX: filter = SkImageFilter::MakeMatrixFilter(make_matrix(), (SkFilterQuality)R(4), make_image_filter()); break; case MATRIX_CONVOLUTION: { SkImageFilter::CropRect cropR(SkRect::MakeWH(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize))); SkISize size = SkISize::Make(R(10)+1, R(10)+1); int arraySize = size.width() * size.height(); SkTArray<SkScalar> kernel(arraySize); for (int i = 0; i < arraySize; ++i) { kernel.push_back() = make_scalar(); } SkIPoint kernelOffset = SkIPoint::Make(R(SkIntToScalar(size.width())), R(SkIntToScalar(size.height()))); filter = SkMatrixConvolutionImageFilter::Make(size, kernel.begin(), make_scalar(), make_scalar(), kernelOffset, (SkMatrixConvolutionImageFilter::TileMode)R(3), R(2) == 1, make_image_filter(), &cropR); break; } case COMPOSE: filter = SkComposeImageFilter::Make(make_image_filter(), make_image_filter()); break; case DISTANT_LIGHT: filter = (R(2) == 1) ? SkLightingImageFilter::MakeDistantLitDiffuse(make_point(), make_color(), make_scalar(), make_scalar(), make_image_filter()) : SkLightingImageFilter::MakeDistantLitSpecular(make_point(), make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter()); break; case POINT_LIGHT: filter = (R(2) == 1) ? SkLightingImageFilter::MakePointLitDiffuse(make_point(), make_color(), make_scalar(), make_scalar(), make_image_filter()) : SkLightingImageFilter::MakePointLitSpecular(make_point(), make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter()); break; case SPOT_LIGHT: filter = (R(2) == 1) ? SkLightingImageFilter::MakeSpotLitDiffuse(SkPoint3::Make(0, 0, 0), make_point(), make_scalar(), make_scalar(), make_color(), make_scalar(), make_scalar(), make_image_filter()) : SkLightingImageFilter::MakeSpotLitSpecular(SkPoint3::Make(0, 0, 0), make_point(), make_scalar(), make_scalar(), make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter()); break; case NOISE: { sk_sp<SkShader> shader((R(2) == 1) ? SkPerlinNoiseShader::MakeFractalNoise(make_scalar(true), make_scalar(true), R(10.0f), make_scalar()) : SkPerlinNoiseShader::MakeTurbulence(make_scalar(true), make_scalar(true), R(10.0f), make_scalar())); SkPaint paint; paint.setShader(shader); SkImageFilter::CropRect cropR(SkRect::MakeWH(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize))); filter = SkPaintImageFilter::Make(paint, &cropR); break; } case DROP_SHADOW: filter = SkDropShadowImageFilter::Make(make_scalar(), make_scalar(), make_scalar(true), make_scalar(true), make_color(), make_shadow_mode(), make_image_filter(), nullptr); break; case MORPHOLOGY: if (R(2) == 1) { filter = SkDilateImageFilter::Make(R(static_cast<float>(kBitmapSize)), R(static_cast<float>(kBitmapSize)), make_image_filter()); } else { filter = SkErodeImageFilter::Make(R(static_cast<float>(kBitmapSize)), R(static_cast<float>(kBitmapSize)), make_image_filter()); } break; case BITMAP: { sk_sp<SkImage> image(SkImage::MakeFromBitmap(make_bitmap())); if (R(2) == 1) { filter = SkImageSource::Make(std::move(image), make_rect(), make_rect(), kHigh_SkFilterQuality); } else { filter = SkImageSource::Make(std::move(image)); } break; } case DISPLACE: filter = SkDisplacementMapEffect::Make(make_channel_selector_type(), make_channel_selector_type(), make_scalar(), make_image_filter(false), make_image_filter()); break; case TILE: filter = SkTileImageFilter::Make(make_rect(), make_rect(), make_image_filter(false)); break; case PICTURE: { SkRTreeFactory factory; SkPictureRecorder recorder; SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), &factory, 0); drawSomething(recordingCanvas); sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); filter = SkPictureImageFilter::Make(pict, make_rect()); break; } case PAINT: { SkImageFilter::CropRect cropR(make_rect()); filter = SkPaintImageFilter::Make(make_paint(), &cropR); break; } default: break; } return (filter || canBeNull) ? filter : make_image_filter(canBeNull); }
static int filter_picture(const SkString& inFile, const SkString& outFile) { SkAutoTDelete<SkPicture> inPicture; SkFILEStream inStream(inFile.c_str()); if (inStream.isValid()) { inPicture.reset(SkPicture::CreateFromStream(&inStream)); } if (NULL == inPicture.get()) { SkDebugf("Could not read file %s\n", inFile.c_str()); return -1; } int localCount[SK_ARRAY_COUNT(gOptTable)]; memset(localCount, 0, sizeof(localCount)); SkDebugCanvas debugCanvas(inPicture->width(), inPicture->height()); debugCanvas.setBounds(inPicture->width(), inPicture->height()); inPicture->draw(&debugCanvas); // delete the initial save and restore since replaying the commands will // re-add them if (debugCanvas.getSize() > 1) { debugCanvas.deleteDrawCommandAt(0); debugCanvas.deleteDrawCommandAt(debugCanvas.getSize()-1); } bool changed = true; int numBefore = debugCanvas.getSize(); while (changed) { changed = false; for (int i = 0; i < debugCanvas.getSize(); ++i) { for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { if ((*gOptTable[opt].fCheck)(&debugCanvas, i)) { (*gOptTable[opt].fApply)(&debugCanvas, i); ++gOptTable[opt].fNumTimesApplied; ++localCount[opt]; if (debugCanvas.getSize() == i) { // the optimization removed all the remaining operations break; } opt = 0; // try all the opts all over again changed = true; } } } } int numAfter = debugCanvas.getSize(); if (!outFile.isEmpty()) { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(inPicture->width(), inPicture->height(), NULL, 0); debugCanvas.draw(canvas); SkAutoTUnref<SkPicture> outPicture(recorder.endRecording()); SkFILEWStream outStream(outFile.c_str()); outPicture->serialize(&outStream); } bool someOptFired = false; for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { if (0 != localCount[opt]) { SkDebugf("%d: %d ", opt, localCount[opt]); someOptFired = true; } } if (!someOptFired) { SkDebugf("No opts fired\n"); } else { SkDebugf("\t before: %d after: %d delta: %d\n", numBefore, numAfter, numBefore-numAfter); } return 0; }
/** * Called only by render_picture(). */ static bool render_picture_internal(const SkString& inputPath, const SkString* writePath, const SkString* mismatchPath, sk_tools::PictureRenderer& renderer, SkBitmap** out) { SkString inputFilename = SkOSPath::Basename(inputPath.c_str()); SkString writePathString; if (writePath && writePath->size() > 0 && !FLAGS_writeEncodedImages) { writePathString.set(*writePath); } SkString mismatchPathString; if (mismatchPath && mismatchPath->size() > 0) { mismatchPathString.set(*mismatchPath); } SkFILEStream inputStream; inputStream.setPath(inputPath.c_str()); if (!inputStream.isValid()) { SkDebugf("Could not open file %s\n", inputPath.c_str()); return false; } SkPicture::InstallPixelRefProc proc; if (FLAGS_deferImageDecoding) { proc = &sk_tools::LazyDecodeBitmap; } else if (FLAGS_writeEncodedImages) { SkASSERT(!FLAGS_writePath.isEmpty()); reset_image_file_base_name(inputFilename); proc = &write_image_to_file; } else { proc = &SkImageDecoder::DecodeMemory; } SkDebugf("deserializing... %s\n", inputPath.c_str()); SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&inputStream, proc)); if (nullptr == picture) { SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str()); return false; } while (FLAGS_bench_record) { SkPictureRecorder recorder; picture->playback(recorder.beginRecording(picture->cullRect().width(), picture->cullRect().height(), nullptr, 0)); SkAutoTUnref<SkPicture> other(recorder.endRecording()); } SkDebugf("drawing... [%f %f %f %f] %s\n", picture->cullRect().fLeft, picture->cullRect().fTop, picture->cullRect().fRight, picture->cullRect().fBottom, inputPath.c_str()); renderer.init(picture, &writePathString, &mismatchPathString, &inputFilename, FLAGS_writeChecksumBasedFilenames, FLAGS_mpd); renderer.setup(); renderer.enableWrites(); bool success = renderer.render(out); if (!success) { SkDebugf("Failed to render %s\n", inputFilename.c_str()); } renderer.end(); return success; }
DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) { SkRecord record; SkRecorder recorder(&record, W, H); SkRect bounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(200)); SkRect clip = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(60)); SkPaint alphaOnlyLayerPaint; alphaOnlyLayerPaint.setColor(0x03000000); // Only alpha. SkPaint translucentLayerPaint; translucentLayerPaint.setColor(0x03040506); // Not only alpha. SkPaint xfermodePaint; xfermodePaint.setXfermodeMode(SkXfermode::kDstIn_Mode); SkPaint colorFilterPaint; colorFilterPaint.setColorFilter( SkColorFilter::CreateModeFilter(SK_ColorLTGRAY, SkXfermode::kSrcIn_Mode))->unref(); SkPaint opaqueFilterLayerPaint; opaqueFilterLayerPaint.setColor(0xFF020202); // Opaque. SkPaint translucentFilterLayerPaint; translucentFilterLayerPaint.setColor(0x0F020202); // Not opaque. SkAutoTUnref<SkPicture> shape; { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(100), SkIntToScalar(100)); SkPaint shapePaint; shapePaint.setColor(SK_ColorWHITE); canvas->drawRect(SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50)), shapePaint); shape.reset(recorder.endRecordingAsPicture()); } translucentFilterLayerPaint.setImageFilter(SkPictureImageFilter::Create(shape))->unref(); int index = 0; { // Any combination of these should cause the pattern to be optimized. SkRect* firstBounds[] = { NULL, &bounds }; SkPaint* firstPaints[] = { NULL, &alphaOnlyLayerPaint }; SkRect* secondBounds[] = { NULL, &bounds }; SkPaint* secondPaints[] = { &opaqueFilterLayerPaint, &translucentFilterLayerPaint }; for (size_t i = 0; i < SK_ARRAY_COUNT(firstBounds); ++ i) { for (size_t j = 0; j < SK_ARRAY_COUNT(firstPaints); ++j) { for (size_t k = 0; k < SK_ARRAY_COUNT(secondBounds); ++k) { for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) { recorder.saveLayer(firstBounds[i], firstPaints[j]); recorder.save(); recorder.clipRect(clip); recorder.saveLayer(secondBounds[k], secondPaints[m]); recorder.restore(); recorder.restore(); recorder.restore(); assert_merge_svg_opacity_and_filter_layers(r, &record, index, true); index += 7; } } } } } // These should cause the pattern to stay unoptimized: struct { SkPaint* firstPaint; SkPaint* secondPaint; } noChangeTests[] = { // No change: NULL filter layer paint not implemented. { &alphaOnlyLayerPaint, NULL }, // No change: layer paint is not alpha-only. { &translucentLayerPaint, &opaqueFilterLayerPaint }, // No change: layer paint has an xfereffect. { &xfermodePaint, &opaqueFilterLayerPaint }, // No change: filter layer paint has an xfereffect. { &alphaOnlyLayerPaint, &xfermodePaint }, // No change: layer paint has a color filter. { &colorFilterPaint, &opaqueFilterLayerPaint }, // No change: filter layer paint has a color filter (until the optimization accounts for // constant color draws that can filter the color). { &alphaOnlyLayerPaint, &colorFilterPaint } }; for (size_t i = 0; i < SK_ARRAY_COUNT(noChangeTests); ++i) { recorder.saveLayer(NULL, noChangeTests[i].firstPaint); recorder.save(); recorder.clipRect(clip); recorder.saveLayer(NULL, noChangeTests[i].secondPaint); recorder.restore(); recorder.restore(); recorder.restore(); assert_merge_svg_opacity_and_filter_layers(r, &record, index, false); index += 7; } // Test the folded alpha value. recorder.saveLayer(NULL, &alphaOnlyLayerPaint); recorder.save(); recorder.clipRect(clip); recorder.saveLayer(NULL, &opaqueFilterLayerPaint); recorder.restore(); recorder.restore(); recorder.restore(); assert_merge_svg_opacity_and_filter_layers(r, &record, index, true); const SkRecords::SaveLayer* saveLayer = assert_type<SkRecords::SaveLayer>(r, record, index + 3); REPORTER_ASSERT(r, saveLayer != NULL); REPORTER_ASSERT(r, saveLayer->paint->getColor() == 0x03020202); index += 7; // Test that currently we do not fold alphas for patterns without the clip. This is just not // implemented. recorder.saveLayer(NULL, &alphaOnlyLayerPaint); recorder.saveLayer(NULL, &opaqueFilterLayerPaint); recorder.restore(); recorder.restore(); SkRecordMergeSvgOpacityAndFilterLayers(&record); assert_type<SkRecords::SaveLayer>(r, record, index); assert_type<SkRecords::SaveLayer>(r, record, index + 1); assert_type<SkRecords::Restore>(r, record, index + 2); assert_type<SkRecords::Restore>(r, record, index + 3); index += 4; }
DEF_TEST(Serialization, reporter) { // Test matrix serialization { SkMatrix matrix = SkMatrix::I(); TestObjectSerialization(&matrix, reporter); } // Test path serialization { SkPath path; TestObjectSerialization(&path, reporter); } // Test region serialization { SkRegion region; TestObjectSerialization(®ion, reporter); } // Test xfermode serialization { TestXfermodeSerialization(reporter); } // Test color filter serialization { TestColorFilterSerialization(reporter); } // Test string serialization { SkString string("string"); TestObjectSerializationNoAlign<SkString, false>(&string, reporter); TestObjectSerializationNoAlign<SkString, true>(&string, reporter); } // Test rrect serialization { // SkRRect does not initialize anything. // An uninitialized SkRRect can be serialized, // but will branch on uninitialized data when deserialized. SkRRect rrect; SkRect rect = SkRect::MakeXYWH(1, 2, 20, 30); SkVector corners[4] = { {1, 2}, {2, 3}, {3,4}, {4,5} }; rrect.setRectRadii(rect, corners); TestAlignment(&rrect, reporter); } // Test readByteArray { unsigned char data[kArraySize] = { 1, 2, 3 }; TestArraySerialization(data, reporter); } // Test readColorArray { SkColor data[kArraySize] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorRED }; TestArraySerialization(data, reporter); } // Test readIntArray { int32_t data[kArraySize] = { 1, 2, 4, 8 }; TestArraySerialization(data, reporter); } // Test readPointArray { SkPoint data[kArraySize] = { {6, 7}, {42, 128} }; TestArraySerialization(data, reporter); } // Test readScalarArray { SkScalar data[kArraySize] = { SK_Scalar1, SK_ScalarHalf, SK_ScalarMax }; TestArraySerialization(data, reporter); } // Test invalid deserializations { SkImageInfo info = SkImageInfo::MakeN32Premul(kBitmapSize, kBitmapSize); SkBitmap validBitmap; validBitmap.setInfo(info); // Create a bitmap with a really large height SkBitmap invalidBitmap; invalidBitmap.setInfo(info.makeWH(info.width(), 1000000000)); // The deserialization should succeed, and the rendering shouldn't crash, // even when the device fails to initialize, due to its size TestBitmapSerialization(validBitmap, invalidBitmap, true, reporter); } // Test simple SkPicture serialization { SkPictureRecorder recorder; draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), nullptr, 0)); SkAutoTUnref<SkPicture> pict(recorder.endRecording()); // Serialize picture SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag); pict->flatten(writer); size_t size = writer.bytesWritten(); SkAutoTMalloc<unsigned char> data(size); writer.writeToMemory(static_cast<void*>(data.get())); // Deserialize picture SkValidatingReadBuffer reader(static_cast<void*>(data.get()), size); SkAutoTUnref<SkPicture> readPict( SkPicture::CreateFromBuffer(reader)); REPORTER_ASSERT(reporter, readPict.get()); } TestPictureTypefaceSerialization(reporter); }
static sk_sp<SkImageFilter> make_image_filter(bool canBeNull) { sk_sp<SkImageFilter> filter; // Add a 1 in 3 chance to get a nullptr input uint8_t i; fuzz->nextRange(&i, 0, 2); if (fuzz->exhausted() || (canBeNull && i == 1)) { return filter; } enum { ALPHA_THRESHOLD, MERGE, COLOR, BLUR, MAGNIFIER, BLENDMODE, OFFSET, MATRIX, MATRIX_CONVOLUTION, COMPOSE, DISTANT_LIGHT, POINT_LIGHT, SPOT_LIGHT, NOISE, DROP_SHADOW, MORPHOLOGY, BITMAP, DISPLACE, TILE, PICTURE, PAINT, NUM_FILTERS }; uint8_t s; fuzz->nextRange(&s, 0, NUM_FILTERS - 1); switch (s) { case ALPHA_THRESHOLD: { SkRegion reg = make_region(); SkScalar innerMin, outerMax; fuzz->next(&innerMin, &outerMax); sk_sp<SkImageFilter> fil = make_image_filter(); filter = SkAlphaThresholdFilter::Make(reg, innerMin, outerMax, fil); break; } case MERGE: { sk_sp<SkImageFilter> filA = make_image_filter(); sk_sp<SkImageFilter> filB = make_image_filter(); filter = SkMergeImageFilter::Make(filA, filB); break; } case COLOR: { sk_sp<SkColorFilter> cf(make_color_filter()); filter = cf ? SkColorFilterImageFilter::Make(std::move(cf), make_image_filter()) : nullptr; break; } case BLUR: { SkScalar sX = make_number(true); SkScalar sY = make_number(true); sk_sp<SkImageFilter> fil = make_image_filter(); filter = SkBlurImageFilter::Make(sX, sY, fil); break; } case MAGNIFIER: { SkRect rect = make_rect(); SkScalar inset = make_number(true); sk_sp<SkImageFilter> fil = make_image_filter(); filter = SkMagnifierImageFilter::Make(rect, inset, fil); break; } case BLENDMODE: { SkBlendMode mode = make_blendmode(); sk_sp<SkImageFilter> filA = make_image_filter(); sk_sp<SkImageFilter> filB = make_image_filter(); filter = SkXfermodeImageFilter::Make(mode, filA, filB, nullptr); break; } case OFFSET: { SkScalar dx, dy; fuzz->next(&dx, &dy); sk_sp<SkImageFilter> fil = make_image_filter(); filter = SkOffsetImageFilter::Make(dx, dy, fil); break; } case MATRIX: { SkMatrix m; init_matrix(&m); int qual; fuzz->nextRange(&qual, 0, SkFilterQuality::kLast_SkFilterQuality - 1); sk_sp<SkImageFilter> fil = make_image_filter(); filter = SkImageFilter::MakeMatrixFilter(m, (SkFilterQuality)qual, fil); break; } case MATRIX_CONVOLUTION: { SkImageFilter::CropRect cropR(SkRect::MakeWH(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize))); int w, h; fuzz->nextRange(&w, 1, 10); fuzz->nextRange(&h, 1, 10); SkISize size = SkISize::Make(w, h); int arraySize = size.width() * size.height(); SkTArray<SkScalar> kernel(arraySize); for (int i = 0; i < arraySize; ++i) { kernel.push_back() = make_number(false); } fuzz->nextRange(&w, 0, size.width() - 1); fuzz->nextRange(&h, 0, size.height() - 1); SkIPoint kernelOffset = SkIPoint::Make(w, h); int mode; fuzz->nextRange(&mode, 0, SkMatrixConvolutionImageFilter::kMax_TileMode - 1); bool convolveAlpha = make_bool(); SkScalar gain, bias; fuzz->next(&gain, &bias); sk_sp<SkImageFilter> fil = make_image_filter(); filter = SkMatrixConvolutionImageFilter::Make(size, kernel.begin(), gain, bias, kernelOffset, (SkMatrixConvolutionImageFilter::TileMode)mode, convolveAlpha, fil, &cropR); break; } case COMPOSE: { sk_sp<SkImageFilter> filA = make_image_filter(); sk_sp<SkImageFilter> filB = make_image_filter(); filter = SkComposeImageFilter::Make(filA, filB); break; } case DISTANT_LIGHT: { SkPoint3 p = make_point(); SkColor c = make_color(); SkScalar ss, kd; fuzz->next(&ss, &kd); int shininess; fuzz->nextRange(&shininess, 0, 9); sk_sp<SkImageFilter> fil = make_image_filter(); filter = make_bool() ? SkLightingImageFilter::MakeDistantLitDiffuse(p, c, ss, kd, fil) : SkLightingImageFilter::MakeDistantLitSpecular(p, c, ss, kd, shininess, fil); break; } case POINT_LIGHT: { SkPoint3 p = make_point(); SkColor c = make_color(); SkScalar ss, kd; fuzz->next(&ss, &kd); int shininess; fuzz->nextRange(&shininess, 0, 9); sk_sp<SkImageFilter> fil = make_image_filter(); filter = make_bool() ? SkLightingImageFilter::MakePointLitDiffuse(p, c, ss, kd, fil) : SkLightingImageFilter::MakePointLitSpecular(p, c, ss, kd, shininess, fil); break; } case SPOT_LIGHT: { SkPoint3 p = make_point(); SkColor c = make_color(); SkScalar se, ca, ss, kd; fuzz->next(&se, &ca, &ss, &kd); int shininess; fuzz->nextRange(&shininess, 0, 9); sk_sp<SkImageFilter> fil = make_image_filter(); filter = make_bool() ? SkLightingImageFilter::MakeSpotLitDiffuse(SkPoint3::Make(0, 0, 0), p, se, ca, c, ss, kd, fil) : SkLightingImageFilter::MakeSpotLitSpecular(SkPoint3::Make(0, 0, 0), p, se, ca, c, ss, kd, shininess, fil); break; } case NOISE: { SkScalar bfx = make_number(true); SkScalar bfy = make_number(true); SkScalar seed = make_number(false); int octaves; fuzz->nextRange(&octaves, 0, 9); sk_sp<SkShader> shader(make_bool() ? SkPerlinNoiseShader::MakeFractalNoise(bfx, bfy, octaves, seed) : SkPerlinNoiseShader::MakeTurbulence(bfx, bfy, octaves, seed)); SkPaint paint; paint.setShader(shader); SkImageFilter::CropRect cropR(SkRect::MakeWH(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize))); filter = SkPaintImageFilter::Make(paint, &cropR); break; } case DROP_SHADOW: { SkScalar dx, dy, sx, sy; fuzz->next(&dx, &dy); sx = make_number(true); sy = make_number(true); SkColor c = make_color(); SkDropShadowImageFilter::ShadowMode mode = make_shadow_mode(); sk_sp<SkImageFilter> fil = make_image_filter(); filter = SkDropShadowImageFilter::Make(dx, dy, sx, sy, c, mode, fil, nullptr); break; } case MORPHOLOGY: { int rx, ry; fuzz->nextRange(&rx, 0, kBitmapSize); fuzz->nextRange(&ry, 0, kBitmapSize); sk_sp<SkImageFilter> fil = make_image_filter(); if (make_bool()) { filter = SkDilateImageFilter::Make(rx, ry, fil); } else { filter = SkErodeImageFilter::Make(rx, ry, fil); } break; } case BITMAP: { sk_sp<SkImage> image(SkImage::MakeFromBitmap(make_bitmap())); if (make_bool()) { filter = SkImageSource::Make(std::move(image), make_rect(), make_rect(), kHigh_SkFilterQuality); } else { filter = SkImageSource::Make(std::move(image)); } break; } case DISPLACE: { SkDisplacementMapEffect::ChannelSelectorType x = make_channel_selector_type(); SkDisplacementMapEffect::ChannelSelectorType y = make_channel_selector_type(); SkScalar scale = make_number(false); sk_sp<SkImageFilter> filA = make_image_filter(false); sk_sp<SkImageFilter> filB = make_image_filter(); filter = SkDisplacementMapEffect::Make(x, y, scale, filA, filB); break; } case TILE: { SkRect src = make_rect(); SkRect dest = make_rect(); sk_sp<SkImageFilter> fil = make_image_filter(false); filter = SkTileImageFilter::Make(src, dest, fil); break; } case PICTURE: { SkRTreeFactory factory; SkPictureRecorder recorder; SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), &factory, 0); drawSomething(recordingCanvas); sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); filter = SkPictureImageFilter::Make(pict, make_rect()); break; } case PAINT: { SkImageFilter::CropRect cropR(make_rect()); filter = SkPaintImageFilter::Make(make_paint(), &cropR); break; } default: break; } return filter; }
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_hierarchical(skiatest::Reporter* reporter) { SkBitmap bm; make_bm(&bm, 10, 10, SK_ColorRED, true); SkPictureRecorder recorder; recorder.beginRecording(10, 10); SkAutoTUnref<SkPicture> childPlain(recorder.endRecording()); REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording()); REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->drawPicture(childPlain); SkAutoTUnref<SkPicture> parentPP(recorder.endRecording()); REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->drawPicture(childWithBitmap); SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording()); REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->drawBitmap(bm, 0, 0); canvas->drawPicture(childPlain); SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording()); REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->drawBitmap(bm, 0, 0); canvas->drawPicture(childWithBitmap); SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording()); REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 } }
static void test_has_text(skiatest::Reporter* reporter) { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(100,100); { canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint()); } SkAutoTUnref<SkPicture> picture(recorder.endRecording()); REPORTER_ASSERT(reporter, !picture->hasText()); SkPoint point = SkPoint::Make(10, 10); canvas = recorder.beginRecording(100,100); { canvas->drawText("Q", 1, point.fX, point.fY, SkPaint()); } picture.reset(recorder.endRecording()); REPORTER_ASSERT(reporter, picture->hasText()); canvas = recorder.beginRecording(100,100); { canvas->drawPosText("Q", 1, &point, SkPaint()); } picture.reset(recorder.endRecording()); REPORTER_ASSERT(reporter, picture->hasText()); canvas = recorder.beginRecording(100,100); { canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint()); } picture.reset(recorder.endRecording()); REPORTER_ASSERT(reporter, picture->hasText()); canvas = recorder.beginRecording(100,100); { SkPath path; path.moveTo(0, 0); path.lineTo(50, 50); canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint()); } picture.reset(recorder.endRecording()); REPORTER_ASSERT(reporter, picture->hasText()); canvas = recorder.beginRecording(100,100); { SkPath path; path.moveTo(0, 0); path.lineTo(50, 50); canvas->drawTextOnPath("Q", 1, path, NULL, SkPaint()); } picture.reset(recorder.endRecording()); REPORTER_ASSERT(reporter, picture->hasText()); // Nest the previous picture inside a new one. canvas = recorder.beginRecording(100,100); { canvas->drawPicture(picture.get()); } picture.reset(recorder.endRecording()); REPORTER_ASSERT(reporter, picture->hasText()); }
// Test out SkPictureRecorder::partialReplay DEF_TEST(PictureRecorder_replay, reporter) { // check save/saveLayer state { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->saveLayer(NULL, NULL); SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); // The extra save and restore comes from the Copy process. check_save_state(reporter, copy, 2, 1, 3); canvas->saveLayer(NULL, NULL); SkAutoTUnref<SkPicture> final(recorder.endRecording()); check_save_state(reporter, final, 1, 2, 3); // The copy shouldn't pick up any operations added after it was made check_save_state(reporter, copy, 2, 1, 3); } // (partially) check leakage of draw ops { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(10, 10); SkRect r = SkRect::MakeWH(5, 5); SkPaint p; canvas->drawRect(r, p); SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); SkBitmap bm; make_bm(&bm, 10, 10, SK_ColorRED, true); r.offset(5.0f, 5.0f); canvas->drawBitmapRectToRect(bm, NULL, r); SkAutoTUnref<SkPicture> final(recorder.endRecording()); REPORTER_ASSERT(reporter, final->willPlayBackBitmaps()); REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID()); // The snapshot shouldn't pick up any operations added after it was made REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); } // Recreate the Android partialReplay test case { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0); create_imbalance(canvas); int expectedSaveCount = canvas->getSaveCount(); SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); check_balance(reporter, copy); REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount()); // End the recording of source to test the picture finalization // process isn't complicated by the partialReplay step SkAutoTUnref<SkPicture> final(recorder.endRecording()); } }
static void test_savelayer_extraction(skiatest::Reporter* reporter) { static const int kWidth = 100; static const int kHeight = 100; // Create complex paint that the bounding box computation code can't // optimize away SkScalar blueToRedMatrix[20] = { 0 }; blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1; SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix)); SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get())); SkPaint complexPaint; complexPaint.setImageFilter(filter); SkAutoTUnref<SkPicture> pict, child; SkRTreeFactory bbhFactory; { SkPictureRecorder recorder; SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight), &bbhFactory, SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag); c->saveLayer(NULL, &complexPaint); c->restore(); child.reset(recorder.endRecording()); } // create a picture with the structure: // 1) // SaveLayer // Restore // 2) // SaveLayer // Translate // SaveLayer w/ bound // Restore // Restore // 3) // SaveLayer w/ copyable paint // Restore // 4) // SaveLayer // DrawPicture (which has a SaveLayer/Restore pair) // Restore // 5) // SaveLayer // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair) // Restore { SkPictureRecorder recorder; SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight), &bbhFactory, SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag); // 1) c->saveLayer(NULL, &complexPaint); // layer #0 c->restore(); // 2) c->saveLayer(NULL, NULL); // layer #1 c->translate(kWidth / 2.0f, kHeight / 2.0f); SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2); c->saveLayer(&r, &complexPaint); // layer #2 c->restore(); c->restore(); // 3) { c->saveLayer(NULL, &complexPaint); // layer #3 c->restore(); } SkPaint layerPaint; layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper // 4) { c->saveLayer(NULL, &layerPaint); // layer #4 c->drawPicture(child); // layer #5 inside picture c->restore(); } // 5 { SkPaint picturePaint; SkMatrix trans; trans.setTranslate(10, 10); c->saveLayer(NULL, &layerPaint); // layer #6 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture c->restore(); } pict.reset(recorder.endRecording()); } // Now test out the SaveLayer extraction if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) { SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key); REPORTER_ASSERT(reporter, data); const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data); REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks()); const SkLayerInfo::BlockInfo& info0 = gpuData->block(0); // The parent/child layers appear in reverse order const SkLayerInfo::BlockInfo& info1 = gpuData->block(2); const SkLayerInfo::BlockInfo& info2 = gpuData->block(1); const SkLayerInfo::BlockInfo& info3 = gpuData->block(3); // The parent/child layers appear in reverse order const SkLayerInfo::BlockInfo& info4 = gpuData->block(5); const SkLayerInfo::BlockInfo& info5 = gpuData->block(4); // The parent/child layers appear in reverse order const SkLayerInfo::BlockInfo& info6 = gpuData->block(7); const SkLayerInfo::BlockInfo& info7 = gpuData->block(6); REPORTER_ASSERT(reporter, NULL == info0.fPicture); REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() && kHeight == info0.fBounds.height()); REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity()); REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity()); REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop); REPORTER_ASSERT(reporter, NULL != info0.fPaint); REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers); REPORTER_ASSERT(reporter, NULL == info1.fPicture); REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() && kHeight/2.0 == info1.fBounds.height()); REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity()); REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity()); REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft && kHeight/2.0 == info1.fBounds.fTop); REPORTER_ASSERT(reporter, NULL == info1.fPaint); REPORTER_ASSERT(reporter, !info1.fIsNested && info1.fHasNestedLayers); // has a nested SL REPORTER_ASSERT(reporter, NULL == info2.fPicture); REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() && kHeight / 2 == info2.fBounds.height()); // bound reduces size REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity()); REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity()); REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated kHeight / 2 == info2.fBounds.fTop); REPORTER_ASSERT(reporter, NULL != info2.fPaint); REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested REPORTER_ASSERT(reporter, NULL == info3.fPicture); REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() && kHeight == info3.fBounds.height()); REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity()); REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity()); REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop); REPORTER_ASSERT(reporter, info3.fPaint); REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers); REPORTER_ASSERT(reporter, NULL == info4.fPicture); REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() && kHeight == info4.fBounds.height()); REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop); REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity()); REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity()); REPORTER_ASSERT(reporter, info4.fPaint); REPORTER_ASSERT(reporter, !info4.fIsNested && info4.fHasNestedLayers); // has a nested SL REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() && kHeight == info5.fBounds.height()); REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop); REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity()); REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity()); REPORTER_ASSERT(reporter, NULL != info5.fPaint); REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested REPORTER_ASSERT(reporter, NULL == info6.fPicture); REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() && kHeight-10 == info6.fBounds.height()); REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop); REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity()); REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity()); REPORTER_ASSERT(reporter, info6.fPaint); REPORTER_ASSERT(reporter, !info6.fIsNested && info6.fHasNestedLayers); // has a nested SL REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() && kHeight == info7.fBounds.height()); REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop); REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity()); REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity()); REPORTER_ASSERT(reporter, NULL != info7.fPaint); REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested } }
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 }; SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0)); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setPathEffect(dash); for (int i = 0; i < 50; ++i) { canvas->drawPath(path, paint); } } SkAutoTUnref<SkPicture> picture(recorder.endRecording()); // path effects currently render an SkPicture undesireable for GPU rendering const char *reason = NULL; REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &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.reset(recorder.endRecording()); // A lot of small AA concave paths should be fine for GPU rendering REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL)); 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.reset(recorder.endRecording()); // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); 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.reset(recorder.endRecording()); // hairline stroked AA concave paths are fine for GPU rendering REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL)); canvas = recorder.beginRecording(100, 100); { SkPaint paint; SkScalar intervals [] = { 10, 20 }; SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25); paint.setPathEffect(pe)->unref(); SkPoint points [2] = { { 0, 0 }, { 100, 0 } }; for (int i = 0; i < 50; ++i) { canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint); } } picture.reset(recorder.endRecording()); // fast-path dashed effects are fine for GPU rendering ... REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL)); canvas = recorder.beginRecording(100, 100); { SkPaint paint; SkScalar intervals [] = { 10, 20 }; SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25); paint.setPathEffect(pe)->unref(); for (int i = 0; i < 50; ++i) { canvas->drawRect(SkRect::MakeWH(10, 10), paint); } } picture.reset(recorder.endRecording()); // ... but only when applied to drawPoint() calls REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); // Nest the previous picture inside a new one. canvas = recorder.beginRecording(100, 100); { canvas->drawPicture(picture.get()); } picture.reset(recorder.endRecording()); REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); }
DEF_TEST(Picture_EmptyBitmap, r) { SkPictureRecorder recorder; test_draw_bitmaps(recorder.beginRecording(10, 10)); SkAutoTUnref<SkPicture> picture(recorder.endRecording()); }
void onDraw(SkCanvas* canvas) override{ SkPaint blackFill; //----------- // Normal paints (no source) SkTArray<SkPaint> paints; create_paints(nullptr, &paints); //----------- // Paints with a PictureImageFilter as a source SkAutoTUnref<SkPicture> pic; { SkPictureRecorder rec; SkCanvas* c = rec.beginRecording(10, 10); c->drawRect(SkRect::MakeWH(10, 10), blackFill); pic.reset(rec.endRecording()); } SkAutoTUnref<SkPictureImageFilter> pif(SkPictureImageFilter::Create(pic)); SkTArray<SkPaint> pifPaints; create_paints(pif, &pifPaints); //----------- // Paints with a SkImageSource as a source SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(10, 10)); { SkPaint p; SkCanvas* temp = surface->getCanvas(); temp->clear(SK_ColorYELLOW); p.setColor(SK_ColorBLUE); temp->drawRect(SkRect::MakeLTRB(5, 5, 10, 10), p); p.setColor(SK_ColorGREEN); temp->drawRect(SkRect::MakeLTRB(5, 0, 10, 5), p); } SkAutoTUnref<SkImage> image(surface->newImageSnapshot()); SkAutoTUnref<SkImageFilter> imageSource(SkImageSource::Create(image)); SkTArray<SkPaint> bmsPaints; create_paints(imageSource, &bmsPaints); //----------- SkASSERT(paints.count() == kNumVertTiles); SkASSERT(paints.count() == pifPaints.count()); SkASSERT(paints.count() == bmsPaints.count()); // horizontal separators for (int i = 1; i < paints.count(); ++i) { canvas->drawLine(0, i*SkIntToScalar(kTileHeight), SkIntToScalar((SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols)*kTileWidth), i*SkIntToScalar(kTileHeight), blackFill); } // vertical separators for (int i = 0; i < (int)SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols; ++i) { canvas->drawLine(SkIntToScalar(i * kTileWidth), 0, SkIntToScalar(i * kTileWidth), SkIntToScalar(paints.count() * kTileWidth), blackFill); } // A column of saveLayers with PictureImageFilters for (int i = 0; i < pifPaints.count(); ++i) { draw_savelayer_with_paint(SkIPoint::Make(0, i*kTileHeight), canvas, pifPaints[i]); } // A column of saveLayers with BitmapSources for (int i = 0; i < pifPaints.count(); ++i) { draw_savelayer_with_paint(SkIPoint::Make(kTileWidth, i*kTileHeight), canvas, bmsPaints[i]); } // Multiple columns with different geometry for (int i = 0; i < (int)SK_ARRAY_COUNT(gDrawMthds); ++i) { for (int j = 0; j < paints.count(); ++j) { draw_geom_with_paint(*gDrawMthds[i], SkIPoint::Make((i+kNumXtraCols) * kTileWidth, j*kTileHeight), canvas, paints[j]); } } }
static SkImageFilter* make_image_filter(bool canBeNull = true) { SkImageFilter* filter = 0; // Add a 1 in 3 chance to get a NULL input if (canBeNull && (R(3) == 1)) { return filter; } enum { ALPHA_THRESHOLD, MERGE, COLOR, LUT3D, BLUR, MAGNIFIER, DOWN_SAMPLE, XFERMODE, OFFSET, MATRIX, MATRIX_CONVOLUTION, COMPOSE, DISTANT_LIGHT, POINT_LIGHT, SPOT_LIGHT, NOISE, DROP_SHADOW, MORPHOLOGY, BITMAP, DISPLACE, TILE, PICTURE, NUM_FILTERS }; switch (R(NUM_FILTERS)) { case ALPHA_THRESHOLD: filter = SkAlphaThresholdFilter::Create(make_region(), make_scalar(), make_scalar()); break; case MERGE: filter = SkMergeImageFilter::Create(make_image_filter(), make_image_filter(), make_xfermode()); break; case COLOR: { SkAutoTUnref<SkColorFilter> cf((R(2) == 1) ? SkColorFilter::CreateModeFilter(make_color(), make_xfermode()) : SkColorFilter::CreateLightingFilter(make_color(), make_color())); filter = cf.get() ? SkColorFilterImageFilter::Create(cf, make_image_filter()) : 0; } break; case LUT3D: { int cubeDimension; SkAutoDataUnref lut3D(make_3Dlut(&cubeDimension, (R(2) == 1), (R(2) == 1), (R(2) == 1))); SkAutoTUnref<SkColorFilter> cf(SkColorCubeFilter::Create(lut3D, cubeDimension)); filter = cf.get() ? SkColorFilterImageFilter::Create(cf, make_image_filter()) : 0; } break; case BLUR: filter = SkBlurImageFilter::Create(make_scalar(true), make_scalar(true), make_image_filter()); break; case MAGNIFIER: filter = SkMagnifierImageFilter::Create(make_rect(), make_scalar(true)); break; case DOWN_SAMPLE: filter = SkDownSampleImageFilter::Create(make_scalar()); break; case XFERMODE: { SkAutoTUnref<SkXfermode> mode(SkXfermode::Create(make_xfermode())); filter = SkXfermodeImageFilter::Create(mode, make_image_filter(), make_image_filter()); } break; case OFFSET: filter = SkOffsetImageFilter::Create(make_scalar(), make_scalar(), make_image_filter()); break; case MATRIX: filter = SkImageFilter::CreateMatrixFilter(make_matrix(), (SkFilterQuality)R(4), make_image_filter()); break; case MATRIX_CONVOLUTION: { SkImageFilter::CropRect cropR(SkRect::MakeWH(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize))); SkISize size = SkISize::Make(R(10)+1, R(10)+1); int arraySize = size.width() * size.height(); SkTArray<SkScalar> kernel(arraySize); for (int i = 0; i < arraySize; ++i) { kernel.push_back() = make_scalar(); } SkIPoint kernelOffset = SkIPoint::Make(R(SkIntToScalar(size.width())), R(SkIntToScalar(size.height()))); filter = SkMatrixConvolutionImageFilter::Create(size, kernel.begin(), make_scalar(), make_scalar(), kernelOffset, (SkMatrixConvolutionImageFilter::TileMode)R(3), R(2) == 1, make_image_filter(), &cropR); } break; case COMPOSE: filter = SkComposeImageFilter::Create(make_image_filter(), make_image_filter()); break; case DISTANT_LIGHT: filter = (R(2) == 1) ? SkLightingImageFilter::CreateDistantLitDiffuse(make_point(), make_color(), make_scalar(), make_scalar(), make_image_filter()) : SkLightingImageFilter::CreateDistantLitSpecular(make_point(), make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter()); break; case POINT_LIGHT: filter = (R(2) == 1) ? SkLightingImageFilter::CreatePointLitDiffuse(make_point(), make_color(), make_scalar(), make_scalar(), make_image_filter()) : SkLightingImageFilter::CreatePointLitSpecular(make_point(), make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter()); break; case SPOT_LIGHT: filter = (R(2) == 1) ? SkLightingImageFilter::CreateSpotLitDiffuse(SkPoint3(0, 0, 0), make_point(), make_scalar(), make_scalar(), make_color(), make_scalar(), make_scalar(), make_image_filter()) : SkLightingImageFilter::CreateSpotLitSpecular(SkPoint3(0, 0, 0), make_point(), make_scalar(), make_scalar(), make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter()); break; case NOISE: { SkAutoTUnref<SkShader> shader((R(2) == 1) ? SkPerlinNoiseShader::CreateFractalNoise( make_scalar(true), make_scalar(true), R(10.0f), make_scalar()) : SkPerlinNoiseShader::CreateTurbulence( make_scalar(true), make_scalar(true), R(10.0f), make_scalar())); SkImageFilter::CropRect cropR(SkRect::MakeWH(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize))); filter = SkRectShaderImageFilter::Create(shader, &cropR); } break; case DROP_SHADOW: filter = SkDropShadowImageFilter::Create(make_scalar(), make_scalar(), make_scalar(true), make_scalar(true), make_color(), make_shadow_mode(), make_image_filter(), NULL); break; case MORPHOLOGY: if (R(2) == 1) { filter = SkDilateImageFilter::Create(R(static_cast<float>(kBitmapSize)), R(static_cast<float>(kBitmapSize)), make_image_filter()); } else { filter = SkErodeImageFilter::Create(R(static_cast<float>(kBitmapSize)), R(static_cast<float>(kBitmapSize)), make_image_filter()); } break; case BITMAP: if (R(2) == 1) { filter = SkBitmapSource::Create(make_bitmap(), make_rect(), make_rect()); } else { filter = SkBitmapSource::Create(make_bitmap()); } break; case DISPLACE: filter = SkDisplacementMapEffect::Create(make_channel_selector_type(), make_channel_selector_type(), make_scalar(), make_image_filter(false), make_image_filter()); break; case TILE: filter = SkTileImageFilter::Create(make_rect(), make_rect(), make_image_filter(false)); break; case PICTURE: { SkRTreeFactory factory; SkPictureRecorder recorder; SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), &factory, 0); drawSomething(recordingCanvas); SkAutoTUnref<SkPicture> pict(recorder.endRecording()); filter = SkPictureImageFilter::Create(pict.get(), make_rect()); } break; default: break; } return (filter || canBeNull) ? filter : make_image_filter(canBeNull); }
DEF_TEST(Serialization, reporter) { // Test matrix serialization { SkMatrix matrix = SkMatrix::I(); TestObjectSerialization(&matrix, reporter); } // Test path serialization { SkPath path; TestObjectSerialization(&path, reporter); } // Test region serialization { SkRegion region; TestObjectSerialization(®ion, reporter); } // Test xfermode serialization { TestXfermodeSerialization(reporter); } // Test color filter serialization { TestColorFilterSerialization(reporter); } // Test string serialization { SkString string("string"); TestObjectSerializationNoAlign<SkString, false>(&string, reporter); TestObjectSerializationNoAlign<SkString, true>(&string, reporter); } // Test rrect serialization { // SkRRect does not initialize anything. // An uninitialized SkRRect can be serialized, // but will branch on uninitialized data when deserialized. SkRRect rrect; SkRect rect = SkRect::MakeXYWH(1, 2, 20, 30); SkVector corners[4] = { {1, 2}, {2, 3}, {3,4}, {4,5} }; rrect.setRectRadii(rect, corners); TestAlignment(&rrect, reporter); } // Test readByteArray { unsigned char data[kArraySize] = { 1, 2, 3 }; TestArraySerialization(data, reporter); } // Test readColorArray { SkColor data[kArraySize] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorRED }; TestArraySerialization(data, reporter); } // Test readIntArray { int32_t data[kArraySize] = { 1, 2, 4, 8 }; TestArraySerialization(data, reporter); } // Test readPointArray { SkPoint data[kArraySize] = { {6, 7}, {42, 128} }; TestArraySerialization(data, reporter); } // Test readScalarArray { SkScalar data[kArraySize] = { SK_Scalar1, SK_ScalarHalf, SK_ScalarMax }; TestArraySerialization(data, reporter); } // Test invalid deserializations { SkImageInfo info = SkImageInfo::MakeN32Premul(kBitmapSize, kBitmapSize); SkBitmap validBitmap; validBitmap.setInfo(info); // Create a bitmap with a really large height SkBitmap invalidBitmap; invalidBitmap.setInfo(info.makeWH(info.width(), 1000000000)); // The deserialization should succeed, and the rendering shouldn't crash, // even when the device fails to initialize, due to its size TestBitmapSerialization(validBitmap, invalidBitmap, true, reporter); } // Test simple SkPicture serialization { SkPictureRecorder recorder; draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), nullptr, 0)); sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); // Serialize picture SkBinaryWriteBuffer writer; pict->flatten(writer); size_t size = writer.bytesWritten(); SkAutoTMalloc<unsigned char> data(size); writer.writeToMemory(static_cast<void*>(data.get())); // Deserialize picture SkValidatingReadBuffer reader(static_cast<void*>(data.get()), size); sk_sp<SkPicture> readPict(SkPicture::MakeFromBuffer(reader)); REPORTER_ASSERT(reporter, readPict.get()); } TestPictureTypefaceSerialization(reporter); // Test SkLightingShader/NormalMapSource serialization { const int kTexSize = 2; SkLights::Builder builder; builder.add(SkLights::Light(SkColor3f::Make(1.0f, 1.0f, 1.0f), SkVector3::Make(1.0f, 0.0f, 0.0f))); builder.add(SkLights::Light(SkColor3f::Make(0.2f, 0.2f, 0.2f))); sk_sp<SkLights> fLights = builder.finish(); SkBitmap diffuse = sk_tool_utils::create_checkerboard_bitmap( kTexSize, kTexSize, sk_tool_utils::color_to_565(0x0), sk_tool_utils::color_to_565(0xFF804020), 8); SkRect bitmapBounds = SkRect::MakeIWH(diffuse.width(), diffuse.height()); SkMatrix matrix; SkRect r = SkRect::MakeWH(SkIntToScalar(kTexSize), SkIntToScalar(kTexSize)); matrix.setRectToRect(bitmapBounds, r, SkMatrix::kFill_ScaleToFit); SkMatrix ctm; ctm.setRotate(45); SkBitmap normals; normals.allocN32Pixels(kTexSize, kTexSize); sk_tool_utils::create_frustum_normal_map(&normals, SkIRect::MakeWH(kTexSize, kTexSize)); sk_sp<SkShader> normalMap = SkShader::MakeBitmapShader(normals, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix); sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap), ctm); sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(diffuse, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix); sk_sp<SkShader> lightingShader = SkLightingShader::Make(diffuseShader, normalSource, fLights); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); lightingShader = SkLightingShader::Make(std::move(diffuseShader), nullptr, fLights); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); lightingShader = SkLightingShader::Make(nullptr, std::move(normalSource), fLights); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); lightingShader = SkLightingShader::Make(nullptr, nullptr, fLights); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); } // Test NormalBevelSource serialization { sk_sp<SkNormalSource> bevelSource = SkNormalSource::MakeBevel( SkNormalSource::BevelType::kLinear, 2.0f, 5.0f); SkAutoTUnref<SkNormalSource>(TestFlattenableSerialization(bevelSource.get(), true, reporter)); // TODO test equality? } }
static sk_sp<SkPicture> make_pic(const std::function<void(SkCanvas*)>& drawer) { SkPictureRecorder rec; drawer(rec.beginRecording(128, 128)); return rec.finishRecordingAsPicture(); }