From 247415969a9a5ed6c83cc09395472416c4b7de7f Mon Sep 17 00:00:00 2001 From: reed Date: Thu, 7 Jan 2016 07:44:35 -0800 Subject: [PATCH] add backdrop option to SaveLayerRec BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1523053003 Review URL: https://codereview.chromium.org/1523053003 --- gm/imagefilters.cpp | 74 +++++++++++++++++++++++++++++++++ include/core/SkCanvas.h | 19 +++++++-- include/private/SkRecords.h | 2 + samplecode/SampleApp.cpp | 9 ++++ samplecode/SampleLayers.cpp | 76 +++++++++++++++++++++++++++++++++- src/core/SkCanvas.cpp | 18 ++++---- src/core/SkPictureFlat.h | 12 +++++- src/core/SkPicturePlayback.cpp | 20 ++++++++- src/core/SkPictureRecord.cpp | 46 +++++++++++++++----- src/core/SkRecordDraw.cpp | 2 +- src/core/SkRecorder.cpp | 3 +- 11 files changed, 252 insertions(+), 29 deletions(-) diff --git a/gm/imagefilters.cpp b/gm/imagefilters.cpp index 5f1ae84949..951a934d82 100644 --- a/gm/imagefilters.cpp +++ b/gm/imagefilters.cpp @@ -104,3 +104,77 @@ DEF_SIMPLE_GM(fast_slow_blurimagefilter, canvas, 620, 260) { canvas->translate(r.width() + 20, 0); } } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Resources.h" +#include "SkBlurImageFilter.h" +#include "SkMatrixConvolutionImageFilter.h" +#include "SkMorphologyImageFilter.h" +#include "SkColorMatrixFilter.h" +#include "SkColorFilterImageFilter.h" +#include "SkRRect.h" + +static void draw_set(SkCanvas* canvas, SkImageFilter* filters[], int count) { + const SkRect r = SkRect::MakeXYWH(30, 30, 200, 200); + const SkScalar offset = 250; + SkScalar dx = 0, dy = 0; + + for (int i = 0; i < count; ++i) { + canvas->save(); + SkRRect rr = SkRRect::MakeRectXY(r.makeOffset(dx, dy), 20, 20); + canvas->clipRRect(rr, SkRegion::kIntersect_Op, true); + canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i], 0 }); + canvas->drawColor(0x40FFFFFF); + canvas->restore(); + canvas->restore(); + + if (0 == dx) { + dx = offset; + } else { + dx = 0; + dy = offset; + } + } +} + +DEF_SIMPLE_GM(savelayer_with_backdrop, canvas, 830, 550) { + SkColorMatrix cm; + cm.setSaturation(10); + SkAutoTUnref cf(SkColorMatrixFilter::Create(cm)); + const SkScalar kernel[] = { 4, 0, 4, 0, -15, 0, 4, 0, 4 }; + SkImageFilter* filters[] = { + SkBlurImageFilter::Create(10, 10), + SkDilateImageFilter::Create(8, 8), + SkMatrixConvolutionImageFilter::Create({ 3, 3 }, kernel, 1, 0, { 0, 0 }, + SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, + true), + SkColorFilterImageFilter::Create(cf), + }; + + const struct { + SkScalar fSx, fSy, fTx, fTy; + } xforms[] = { + { 1, 1, 0, 0 }, + { 0.5f, 0.5f, 530, 0 }, + { 0.25f, 0.25f, 530, 275 }, + { 0.125f, 0.125f, 530, 420 }, + }; + + SkPaint paint; + paint.setFilterQuality(kMedium_SkFilterQuality); + SkAutoTUnref image(GetResourceAsImage("mandrill_512.png")); + + canvas->translate(20, 20); + for (const auto& xform : xforms) { + canvas->save(); + canvas->translate(xform.fTx, xform.fTy); + canvas->scale(xform.fSx, xform.fSy); + canvas->drawImage(image, 0, 0, &paint); + draw_set(canvas, filters, SK_ARRAY_COUNT(filters)); + canvas->restore(); + } + + for (auto& filter : filters) { + filter->unref(); + } +} diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index d046b8ef4c..d1de626315 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -422,16 +422,27 @@ public: typedef uint32_t SaveLayerFlags; struct SaveLayerRec { - SaveLayerRec() : fBounds(nullptr), fPaint(nullptr), fSaveLayerFlags(0) {} + SaveLayerRec() + : fBounds(nullptr), fPaint(nullptr), fBackdrop(nullptr), fSaveLayerFlags(0) + {} SaveLayerRec(const SkRect* bounds, const SkPaint* paint, SaveLayerFlags saveLayerFlags = 0) : fBounds(bounds) , fPaint(paint) + , fBackdrop(nullptr) + , fSaveLayerFlags(saveLayerFlags) + {} + SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop, + SaveLayerFlags saveLayerFlags) + : fBounds(bounds) + , fPaint(paint) + , fBackdrop(backdrop) , fSaveLayerFlags(saveLayerFlags) {} - const SkRect* fBounds; // optional - const SkPaint* fPaint; // optional - SaveLayerFlags fSaveLayerFlags; + const SkRect* fBounds; // optional + const SkPaint* fPaint; // optional + const SkImageFilter* fBackdrop; // optional + SaveLayerFlags fSaveLayerFlags; }; int saveLayer(const SaveLayerRec&); diff --git a/include/private/SkRecords.h b/include/private/SkRecords.h index a2e9030b23..ecd73a12d9 100644 --- a/include/private/SkRecords.h +++ b/include/private/SkRecords.h @@ -10,6 +10,7 @@ #include "SkCanvas.h" #include "SkDrawable.h" +#include "SkImageFilter.h" #include "SkMatrix.h" #include "SkPath.h" #include "SkPicture.h" @@ -197,6 +198,7 @@ RECORD(Save, 0); RECORD(SaveLayer, 0, Optional bounds; Optional paint; + RefBox backdrop; SkCanvas::SaveLayerFlags saveLayerFlags); RECORD(SetMatrix, 0, diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp index d200e295fb..c7a37e786c 100644 --- a/samplecode/SampleApp.cpp +++ b/samplecode/SampleApp.cpp @@ -1396,6 +1396,15 @@ void SampleWindow::afterChildren(SkCanvas* orig) { if (fUseMPD) { SkAutoTUnref picture(fRecorder.endRecording()); + + if (false) { + SkDynamicMemoryWStream wstream; + picture->serialize(&wstream); + + SkAutoTDelete rstream(wstream.detachAsStream()); + picture.reset(SkPicture::CreateFromStream(rstream)); + } + if (true) { if (true) { SkImageInfo info; diff --git a/samplecode/SampleLayers.cpp b/samplecode/SampleLayers.cpp index 52bf5b357b..f8ebd7c94f 100644 --- a/samplecode/SampleLayers.cpp +++ b/samplecode/SampleLayers.cpp @@ -232,8 +232,80 @@ protected: private: typedef SkView INHERITED; }; +DEF_SAMPLE( return new LayersView; ) ////////////////////////////////////////////////////////////////////////////// -static SkView* MyFactory() { return new LayersView; } -static SkViewRegister reg(MyFactory); +#include "SkBlurImageFilter.h" +#include "SkMatrixConvolutionImageFilter.h" +#include "SkMorphologyImageFilter.h" + +#include "Resources.h" +#include "SkAnimTimer.h" + +class BackdropView : public SampleView { + SkPoint fCenter; + SkScalar fAngle; + SkAutoTUnref fImage; + SkAutoTUnref fFilter; +public: + BackdropView() { + fCenter.set(200, 150); + fAngle = 0; + fImage.reset(GetResourceAsImage("mandrill_512.png")); + fFilter.reset(SkDilateImageFilter::Create(8, 8)); + } + +protected: + // overrides from SkEventSink + bool onQuery(SkEvent* evt) override { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Backdrop"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void onDrawContent(SkCanvas* canvas) override { + canvas->drawImage(fImage, 0, 0, nullptr); + + const SkScalar w = 250; + const SkScalar h = 150; + SkPath path; + path.addOval(SkRect::MakeXYWH(-w/2, -h/2, w, h)); + SkMatrix m; + m.setRotate(fAngle); + m.postTranslate(fCenter.x(), fCenter.y()); + path.transform(m); + + canvas->clipPath(path, SkRegion::kIntersect_Op, true); + const SkRect bounds = path.getBounds(); + + SkPaint paint; + paint.setAlpha(0xCC); + canvas->saveLayer({ &bounds, &paint, fFilter, 0 }); + + canvas->restore(); + } + + bool onAnimate(const SkAnimTimer& timer) override { + fAngle = SkDoubleToScalar(fmod(timer.secs() * 360 / 5, 360)); + return true; + } + + SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override { + this->inval(nullptr); + return new Click(this); + } + + bool onClick(Click* click) override { + this->inval(nullptr); + fCenter = click->fCurr; + return this->INHERITED::onClick(click); + } + +private: + typedef SampleView INHERITED; +}; +DEF_SAMPLE( return new BackdropView; ) + diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index e5ca9ce6c1..bc354ab3c3 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -114,7 +114,6 @@ bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() { // experimental for faster tiled drawing... //#define SK_ENABLE_CLIP_QUICKREJECT - //#define SK_TRACE_SAVERESTORE #ifdef SK_TRACE_SAVERESTORE @@ -481,7 +480,7 @@ public: // Make rawBounds include all paint outsets except for those due to image filters. rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage); } - (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp, 0), + (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp), SkCanvas::kFullLayer_SaveLayerStrategy); fTempLayerForImageFilter = true; // we remove the imagefilter/xfermode inside doNext() @@ -1173,7 +1172,8 @@ int SkCanvas::saveLayer(const SaveLayerRec& origRec) { return this->getSaveCount() - 1; } -static void draw_filter_into_device(SkBaseDevice* src, SkImageFilter* filter, SkBaseDevice* dst) { +static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter, + SkBaseDevice* dst, const SkMatrix& ctm) { SkBitmap srcBM; @@ -1198,9 +1198,12 @@ static void draw_filter_into_device(SkBaseDevice* src, SkImageFilter* filter, Sk SkCanvas c(dst); + SkAutoTUnref localF(filter->newWithLocalMatrix(ctm)); SkPaint p; - p.setImageFilter(filter); - c.drawBitmap(srcBM, 0, 0, &p); + p.setImageFilter(localF); + const SkScalar x = SkIntToScalar(src->getOrigin().x()); + const SkScalar y = SkIntToScalar(src->getOrigin().y()); + c.drawBitmap(srcBM, x, y, &p); } void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) { @@ -1268,11 +1271,10 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra } device = newDev; } - device->setOrigin(ir.fLeft, ir.fTop); - if (0) { - draw_filter_into_device(fMCRec->fTopLayer->fDevice, nullptr, device); + if (rec.fBackdrop) { + draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix); } DeviceCM* layer = diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h index cbacc8e664..4eee04fcda 100644 --- a/src/core/SkPictureFlat.h +++ b/src/core/SkPictureFlat.h @@ -75,9 +75,10 @@ enum DrawType { DRAW_IMAGE_NINE, DRAW_IMAGE_RECT, - SAVE_LAYER_SAVELAYERFLAGS, + SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016, + SAVE_LAYER_SAVELAYERREC, - LAST_DRAWTYPE_ENUM = SAVE_LAYER_SAVELAYERFLAGS, + LAST_DRAWTYPE_ENUM = SAVE_LAYER_SAVELAYERREC, }; // In the 'match' method, this constant will match any flavor of DRAW_BITMAP* @@ -95,6 +96,13 @@ enum DrawAtlasFlags { DRAW_ATLAS_HAS_CULL = 1 << 1, }; +enum SaveLayerRecFlatFlags { + SAVELAYERREC_HAS_BOUNDS = 1 << 0, + SAVELAYERREC_HAS_PAINT = 1 << 1, + SAVELAYERREC_HAS_BACKDROP = 1 << 2, + SAVELAYERREC_HAS_FLAGS = 1 << 3, +}; + /////////////////////////////////////////////////////////////////////////////// // clipparams are packed in 5 bits // doAA:1 | regionOp:4 diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index 82b8f0e578..4b028f714e 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -483,11 +483,29 @@ void SkPicturePlayback::handleOp(SkReader32* reader, auto flags = SkCanvas::LegacySaveFlagsToSaveLayerFlags(reader->readInt()); canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, flags)); } break; - case SAVE_LAYER_SAVELAYERFLAGS: { + case SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016: { const SkRect* boundsPtr = get_rect_ptr(reader); const SkPaint* paint = fPictureData->getPaint(reader); canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, reader->readInt())); } break; + case SAVE_LAYER_SAVELAYERREC: { + SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, 0); + const uint32_t flatFlags = reader->readInt(); + if (flatFlags & SAVELAYERREC_HAS_BOUNDS) { + rec.fBounds = &reader->skipT(); + } + if (flatFlags & SAVELAYERREC_HAS_PAINT) { + rec.fPaint = fPictureData->getPaint(reader); + } + if (flatFlags & SAVELAYERREC_HAS_BACKDROP) { + const SkPaint* paint = fPictureData->getPaint(reader); + rec.fBackdrop = paint->getImageFilter(); + } + if (flatFlags & SAVELAYERREC_HAS_FLAGS) { + rec.fSaveLayerFlags = reader->readInt(); + } + canvas->saveLayer(rec); + } break; case SCALE: { SkScalar sx = reader->readScalar(); SkScalar sy = reader->readScalar(); diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index 7907ee0d29..2822a1ac16 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -76,19 +76,44 @@ SkCanvas::SaveLayerStrategy SkPictureRecord::getSaveLayerStrategy(const SaveLaye void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) { fContentInfo.onSaveLayer(); - // op + bool for 'bounds' + // op + flatflags size_t size = 2 * kUInt32Size; + uint32_t flatFlags = 0; + if (rec.fBounds) { - size += sizeof(*rec.fBounds); // + rect + flatFlags |= SAVELAYERREC_HAS_BOUNDS; + size += sizeof(*rec.fBounds); + } + if (rec.fPaint) { + flatFlags |= SAVELAYERREC_HAS_PAINT; + size += sizeof(uint32_t); // index + } + if (rec.fBackdrop) { + flatFlags |= SAVELAYERREC_HAS_BACKDROP; + size += sizeof(uint32_t); // (paint) index + } + if (rec.fSaveLayerFlags) { + flatFlags |= SAVELAYERREC_HAS_FLAGS; + size += sizeof(uint32_t); } - // + paint index + flags - size += 2 * kUInt32Size; - - size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERFLAGS, &size); - this->addRectPtr(rec.fBounds); - this->addPaintPtr(rec.fPaint); - this->addInt(rec.fSaveLayerFlags); + const size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERREC, &size); + this->addInt(flatFlags); + if (flatFlags & SAVELAYERREC_HAS_BOUNDS) { + this->addRect(*rec.fBounds); + } + if (flatFlags & SAVELAYERREC_HAS_PAINT) { + this->addPaintPtr(rec.fPaint); + } + if (flatFlags & SAVELAYERREC_HAS_BACKDROP) { + // overkill, but we didn't already track single flattenables, so using a paint for that + SkPaint paint; + paint.setImageFilter(const_cast(rec.fBackdrop)); + this->addPaint(paint); + } + if (flatFlags & SAVELAYERREC_HAS_FLAGS) { + this->addInt(rec.fSaveLayerFlags); + } this->validate(initialOffset, size); } @@ -224,7 +249,8 @@ void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t uint32_t opSize; DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize); SkASSERT(SAVE_LAYER_SAVEFLAGS_DEPRECATED != drawOp); - SkASSERT(SAVE == drawOp || SAVE_LAYER_SAVELAYERFLAGS == drawOp); + SkASSERT(SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016 != drawOp); + SkASSERT(SAVE == drawOp || SAVE_LAYER_SAVELAYERREC == drawOp); } #endif } diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index 01846676b6..5ca9517d3f 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -78,7 +78,7 @@ template <> void Draw::draw(const NoOp&) {} #define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; } DRAW(Restore, restore()); DRAW(Save, save()); -DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds, r.paint, r.saveLayerFlags))); +DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds, r.paint, r.backdrop, r.saveLayerFlags))); DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix))); DRAW(Concat, concat(r.matrix)); diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp index 67429a691e..01c28dfc2b 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -337,7 +337,8 @@ void SkRecorder::willSave() { } SkCanvas::SaveLayerStrategy SkRecorder::getSaveLayerStrategy(const SaveLayerRec& rec) { - APPEND(SaveLayer, this->copy(rec.fBounds), this->copy(rec.fPaint), rec.fSaveLayerFlags); + APPEND(SaveLayer, + this->copy(rec.fBounds), this->copy(rec.fPaint), rec.fBackdrop, rec.fSaveLayerFlags); return SkCanvas::kNoLayer_SaveLayerStrategy; }