From: Florin Malita Date: Fri, 28 Apr 2017 17:48:37 +0000 (-0400) Subject: Initial clip-mask-layer support X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~36^2~562 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=53f77bd4fdd76525b66b7f26d1c5c550858120df;p=platform%2Fupstream%2FlibSkiaSharp.git Initial clip-mask-layer support SkBitmapDevice-only implementation. Will add A8 fast path specializations in a follow-up. Change-Id: I2ccb1ffba3689e92ac90a23e94737471dfb121a1 BUG=skia:6005 Change-Id: I2ccb1ffba3689e92ac90a23e94737471dfb121a1 Reviewed-on: https://skia-review.googlesource.com/14183 Reviewed-by: Mike Reed Commit-Queue: Florin Malita --- diff --git a/gm/imagefilters.cpp b/gm/imagefilters.cpp index 8855afd..dd6287b 100644 --- a/gm/imagefilters.cpp +++ b/gm/imagefilters.cpp @@ -123,7 +123,7 @@ static void draw_set(SkCanvas* canvas, sk_sp filters[], int count canvas->save(); SkRRect rr = SkRRect::MakeRectXY(r.makeOffset(dx, dy), 20, 20); canvas->clipRRect(rr, true); - canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), 0 }); + canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), nullptr, nullptr, 0 }); canvas->drawColor(0x40FFFFFF); canvas->restore(); canvas->restore(); diff --git a/gm/savelayer.cpp b/gm/savelayer.cpp index 98a00ca..245785e 100644 --- a/gm/savelayer.cpp +++ b/gm/savelayer.cpp @@ -8,7 +8,6 @@ #include "gm.h" #include "sk_tool_utils.h" - static const uint32_t SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag = 1U << 31; // This GM tests out the deprecated Android-specific unclipped saveLayer "feature". @@ -17,7 +16,8 @@ static const uint32_t SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag = 1U << 31; static void save_layer_unclipped(SkCanvas* canvas, SkScalar l, SkScalar t, SkScalar r, SkScalar b) { SkRect rect = SkRect::MakeLTRB(l, t, r, b); - canvas->saveLayer({ &rect, nullptr, nullptr, SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag }); + canvas->saveLayer({ &rect, nullptr, nullptr, nullptr, nullptr, + SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag }); } static void do_draw(SkCanvas* canvas) { @@ -94,8 +94,8 @@ DEF_SIMPLE_GM(picture_savelayer, canvas, 320, 640) { for(int i = 1; i < 2; ++i) { canvas->translate(100 * i, 0); auto flag = i ? SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag : 0; - canvas->saveLayer({ &rect1, &paint1, nullptr, flag}); - canvas->saveLayer({ &rect2, &paint2, nullptr, flag}); + canvas->saveLayer({ &rect1, &paint1, nullptr, nullptr, nullptr, flag}); + canvas->saveLayer({ &rect2, &paint2, nullptr, nullptr, nullptr, flag}); canvas->drawRect(rect3, paint3); canvas->restore(); canvas->restore(); @@ -122,3 +122,118 @@ DEF_SIMPLE_GM(savelayer_initfromprev, canvas, 256, 256) { canvas->restore(); }; +#include "SkBlurImageFilter.h" +#include "SkGradientShader.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkSurface.h" + +static void draw_mask(SkCanvas* canvas, int size) { + const SkScalar cx = size * SK_ScalarHalf, + cy = cx; + const SkColor colors[] = { 0x00000000, 0xffff0000, 0x00000000, 0xffff0000, 0x00000000, + 0xffff0000, 0x00000000, 0xffff0000, 0x00000000 }; + + SkPaint paint; + paint.setAntiAlias(true); + paint.setShader(SkGradientShader::MakeSweep(cx, cy, colors, nullptr, SK_ARRAY_COUNT(colors))); + canvas->drawPaint(paint); + + paint.setShader(SkGradientShader::MakeRadial({cx, cy}, size / 4, colors, nullptr, 2, + SkShader::kClamp_TileMode)); + canvas->drawCircle(cx, cy, size / 4, paint); +} + +DEF_SIMPLE_GM(savelayer_clipmask, canvas, 1200, 1200) { + static constexpr int kSize = 100; + static constexpr SkRect kLayerBounds = { kSize * 0.25f, kSize * 0.25f, + kSize * 0.75f, kSize * 0.75f }; + static constexpr struct { + const SkRect* bounds; + const SkScalar matrix[9]; + } kConfigs[] = { + { nullptr, { 1 , 0 , 0, 0 , 1 , 0, 0, 0, 1 } }, + { nullptr, { 2 , 0 , 0, 0 , 2 , 0, 0, 0, 1 } }, + { nullptr, { 2 , 0 , -50, 0 , 2 , -50, 0, 0, 1 } }, + { nullptr, { 0.707f, -0.707f, 50, 0.707f, 0.707f, -20, 0, 0, 1 } }, + { nullptr, { 0.5f , 0 , 25, 0 , 0.5f , 25, 0, 0, 1 } }, + + { &kLayerBounds, { 1 , 0 , 0, 0 , 1 , 0, 0, 0, 1 } }, + { &kLayerBounds, { 2 , 0 , 0, 0 , 2 , 0, 0, 0, 1 } }, + { &kLayerBounds, { 2 , 0 , -50, 0 , 2 , -50, 0, 0, 1 } }, + { &kLayerBounds, { 0.707f, -0.707f, 50, 0.707f, 0.707f, -20, 0, 0, 1 } }, + { &kLayerBounds, { 0.5f , 0 , 25, 0 , 0.5f , 25, 0, 0, 1 } }, + }; + + using MaskMakerFunc = sk_sp (*)(int size); + static const MaskMakerFunc kMaskMakers[] = { + [](int size) -> sk_sp { + auto surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(size, size)); + draw_mask(surf->getCanvas(), size); + return surf->makeImageSnapshot(); + }, + + [](int size) -> sk_sp { + auto surf = SkSurface::MakeRasterN32Premul(size, size); + draw_mask(surf->getCanvas(), size); + return surf->makeImageSnapshot(); + }, + + [](int size) -> sk_sp { + SkPictureRecorder recorder; + draw_mask(recorder.beginRecording(size, size), size); + return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), + SkISize::Make(size, size), + nullptr, nullptr, + SkImage::BitDepth::kU8, + SkColorSpace::MakeSRGB()); + } + }; + + using PaintMakerFunc = SkPaint (*)(); + static const PaintMakerFunc kPaintMakers[] = { + []() -> SkPaint { return SkPaint(); }, + []() -> SkPaint { + SkPaint p; p.setImageFilter(SkBlurImageFilter::Make(2, 2, nullptr)); return p; + }, + []() -> SkPaint { SkPaint p; p.setBlendMode(SkBlendMode::kSrcOut); return p; }, + }; + + canvas->drawColor(0xffcccccc); + + SkMatrix clipMatrix; + SkCanvas::SaveLayerRec rec; + rec.fClipMatrix = &clipMatrix; + + for (const auto& paintMaker : kPaintMakers) { + auto layerPaint = paintMaker(); + rec.fPaint = &layerPaint; + + for (const auto& maskMaker : kMaskMakers) { + maskMaker(kSize); + rec.fClipMask = maskMaker(kSize); + + canvas->save(); + for (const auto cfg : kConfigs) { + rec.fBounds = cfg.bounds; + clipMatrix.set9(cfg.matrix); + canvas->saveLayer(rec); + + SkPaint paint; + paint.setColor(0xff0000ff); + canvas->drawRect(SkRect::MakeWH(50, 50), paint); + paint.setColor(0xffff0000); + canvas->drawRect(SkRect::MakeXYWH(50, 0, 50, 50), paint); + paint.setColor(0xff00ff00); + canvas->drawRect(SkRect::MakeXYWH(50, 50, 50, 50), paint); + paint.setColor(0xffffff00); + canvas->drawRect(SkRect::MakeXYWH(0, 50, 50, 50), paint); + + canvas->restore(); + canvas->translate(120, 0); + } + canvas->restore(); + canvas->translate(0, 120); + } + } +} diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 43cf4f7..d2f75fe 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -323,27 +323,29 @@ public: typedef uint32_t SaveLayerFlags; struct SaveLayerRec { - SaveLayerRec() - : fBounds(nullptr), fPaint(nullptr), fBackdrop(nullptr), fSaveLayerFlags(0) - {} + SaveLayerRec() {} 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, + sk_sp clipMask, const SkMatrix* clipMatrix, SaveLayerFlags saveLayerFlags) : fBounds(bounds) , fPaint(paint) , fBackdrop(backdrop) + , fClipMask(std::move(clipMask)) + , fClipMatrix(clipMatrix) , fSaveLayerFlags(saveLayerFlags) {} - const SkRect* fBounds; // optional - const SkPaint* fPaint; // optional - const SkImageFilter* fBackdrop; // optional - SaveLayerFlags fSaveLayerFlags; + const SkRect* fBounds = nullptr; // optional + const SkPaint* fPaint = nullptr; // optional + const SkImageFilter* fBackdrop = nullptr; // optional + sk_sp fClipMask; // optional + const SkMatrix* fClipMatrix = nullptr; // optional -- only used with fClipMask + SaveLayerFlags fSaveLayerFlags = 0; }; int saveLayer(const SaveLayerRec&); @@ -1489,7 +1491,7 @@ private: enum { kMCRecSize = 128, // most recent measurement kMCRecCount = 32, // common depth for save/restores - kDeviceCMSize = 184, // most recent measurement + kDeviceCMSize = 224, // most recent measurement }; intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)]; intptr_t fDeviceCMStorage[kDeviceCMSize / sizeof(intptr_t)]; @@ -1561,7 +1563,8 @@ private: SrcRectConstraint); void internalDrawPaint(const SkPaint& paint); void internalSaveLayer(const SaveLayerRec&, SaveLayerStrategy); - void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*); + void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*, SkImage* clipImage, + const SkMatrix& clipMatrix); // shared by save() and saveLayer() void internalSave(); diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index 133d3c9..70f2acb 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -200,10 +200,11 @@ private: // V50: SkXfermode -> SkBlendMode // V51: more SkXfermode -> SkBlendMode // V52: Remove SkTextBlob::fRunCount + // V53: SaveLayerRec clip mask // Only SKPs within the min/current picture version range (inclusive) can be read. static const uint32_t MIN_PICTURE_VERSION = 35; // Produced by Chrome M39. - static const uint32_t CURRENT_PICTURE_VERSION = 52; + static const uint32_t CURRENT_PICTURE_VERSION = 53; static_assert(MIN_PICTURE_VERSION <= 41, "Remove kFontFileName and related code from SkFontDescriptor.cpp."); diff --git a/include/private/SkRecords.h b/include/private/SkRecords.h index ac492ec..f1886f3 100644 --- a/include/private/SkRecords.h +++ b/include/private/SkRecords.h @@ -180,6 +180,8 @@ RECORD(SaveLayer, kHasPaint_Tag, Optional bounds; Optional paint; sk_sp backdrop; + sk_sp clipMask; + Optional clipMatrix; SkCanvas::SaveLayerFlags saveLayerFlags); RECORD(SetMatrix, 0, diff --git a/samplecode/SampleLayers.cpp b/samplecode/SampleLayers.cpp index ab75ddb..3ba6d69 100644 --- a/samplecode/SampleLayers.cpp +++ b/samplecode/SampleLayers.cpp @@ -260,7 +260,7 @@ protected: SkPaint paint; paint.setAlpha(0xCC); - canvas->saveLayer({ &bounds, &paint, fFilter.get(), 0 }); + canvas->saveLayer({ &bounds, &paint, fFilter.get(), nullptr, nullptr, 0 }); canvas->restore(); } diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp index 09f25fb..f628621 100644 --- a/src/core/SkBitmapDevice.cpp +++ b/src/core/SkBitmapDevice.cpp @@ -20,6 +20,7 @@ #include "SkShader.h" #include "SkSpecialImage.h" #include "SkSurface.h" +#include "SkTLazy.h" #include "SkVertices.h" class SkColorTable; @@ -387,35 +388,71 @@ void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPain /////////////////////////////////////////////////////////////////////////////// -void SkBitmapDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y, - const SkPaint& paint) { - SkASSERT(!srcImg->isTextureBacked()); +void SkBitmapDevice::drawSpecial(SkSpecialImage* src, int x, int y, const SkPaint& origPaint, + SkImage* clipImage, const SkMatrix& clipMatrix) { + SkASSERT(!src->isTextureBacked()); - SkBitmap resultBM; + sk_sp filteredImage; + SkTCopyOnFirstWrite paint(origPaint); - SkImageFilter* filter = paint.getImageFilter(); - if (filter) { + if (SkImageFilter* filter = paint->getImageFilter()) { SkIPoint offset = SkIPoint::Make(0, 0); - SkMatrix matrix = this->ctm(); - matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); + const SkMatrix matrix = SkMatrix::Concat( + SkMatrix::MakeTrans(SkIntToScalar(-x), SkIntToScalar(-y)), this->ctm()); const SkIRect clipBounds = fRCStack.rc().getBounds().makeOffset(-x, -y); sk_sp cache(this->getImageFilterCache()); SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties); - sk_sp resultImg(filter->filterImage(srcImg, ctx, &offset)); - if (resultImg) { - SkPaint tmpUnfiltered(paint); - tmpUnfiltered.setImageFilter(nullptr); - if (resultImg->getROPixels(&resultBM)) { - this->drawSprite(resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered); - } + filteredImage = filter->filterImage(src, ctx, &offset); + if (!filteredImage) { + return; } - } else { - if (srcImg->getROPixels(&resultBM)) { - this->drawSprite(resultBM, x, y, paint); + + src = filteredImage.get(); + paint.writable()->setImageFilter(nullptr); + x += offset.x(); + y += offset.y(); + } + + if (!clipImage) { + SkBitmap resultBM; + if (src->getROPixels(&resultBM)) { + this->drawSprite(resultBM, x, y, *paint); } + return; + } + + // Clip image case. + sk_sp srcImage(src->asImage()); + if (!srcImage) { + return; } + + const SkMatrix totalMatrix = SkMatrix::Concat(this->ctm(), clipMatrix); + + SkRect clipBounds; + totalMatrix.mapRect(&clipBounds, SkRect::Make(clipImage->bounds())); + const SkIRect srcBounds = srcImage->bounds().makeOffset(x, y); + + SkIRect maskBounds = fRCStack.rc().getBounds(); + if (!maskBounds.intersect(clipBounds.roundOut()) || !maskBounds.intersect(srcBounds)) { + return; + } + + sk_sp surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(maskBounds.width(), + maskBounds.height())); + SkCanvas* canvas = surf->getCanvas(); + canvas->translate(-maskBounds.x(), -maskBounds.y()); + canvas->concat(totalMatrix); + canvas->drawImage(clipImage, 0, 0); + sk_sp mask = surf->makeImageSnapshot(); + + const SkMatrix m = SkMatrix::MakeTrans(x - maskBounds.x(), y - maskBounds.y()); + paint.writable()->setShader(srcImage->makeShader(&m)); + + SkAutoDeviceCTMRestore adctmr(this, SkMatrix::I()); + this->drawImage(mask.get(), maskBounds.x(), maskBounds.y(), *paint); } sk_sp SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) { diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h index 8055b9d..3e9473d 100644 --- a/src/core/SkBitmapDevice.h +++ b/src/core/SkBitmapDevice.h @@ -112,7 +112,8 @@ protected: /////////////////////////////////////////////////////////////////////////// - void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override; + void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&, + SkImage*, const SkMatrix&) override; sk_sp makeSpecial(const SkBitmap&) override; sk_sp makeSpecial(const SkImage*) override; sk_sp snapSpecial() override; diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index a54cf44..c7e1d6e 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -253,12 +253,17 @@ struct DeviceCM { SkRasterClip fClip; std::unique_ptr fPaint; // may be null (in the future) SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer + sk_sp fClipImage; + SkMatrix fClipMatrix; - DeviceCM(sk_sp device, const SkPaint* paint, const SkMatrix& stashed) + DeviceCM(sk_sp device, const SkPaint* paint, const SkMatrix& stashed, + sk_sp clipImage, const SkMatrix* clipMatrix) : fNext(nullptr) , fDevice(std::move(device)) , fPaint(paint ? skstd::make_unique(*paint) : nullptr) , fStashedMatrix(stashed) + , fClipImage(std::move(clipImage)) + , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I()) {} void reset(const SkIRect& bounds) { @@ -649,7 +654,7 @@ SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) { SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage)); fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage; - new (fDeviceCMStorage) DeviceCM(sk_ref_sp(device), nullptr, fMCRec->fMatrix); + new (fDeviceCMStorage) DeviceCM(sk_ref_sp(device), nullptr, fMCRec->fMatrix, nullptr, nullptr); fMCRec->fTopLayer = fMCRec->fLayer; @@ -1010,13 +1015,14 @@ int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPai } int SkCanvas::saveLayer(const SaveLayerRec& origRec) { - SaveLayerRec rec(origRec); + SkTCopyOnFirstWrite rec(origRec); if (gIgnoreSaveLayerBounds) { - rec.fBounds = nullptr; + rec.writable()->fBounds = nullptr; } - SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec); + + SaveLayerStrategy strategy = this->getSaveLayerStrategy(*rec); fSaveCount += 1; - this->internalSaveLayer(rec, strategy); + this->internalSaveLayer(*rec, strategy); return this->getSaveCount() - 1; } @@ -1041,7 +1047,7 @@ void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filt int y = src->getOrigin().y() - dstOrigin.y(); auto special = src->snapSpecial(); if (special) { - dst->drawSpecial(special.get(), x, y, p); + dst->drawSpecial(special.get(), x, y, p, nullptr, SkMatrix::I()); } } @@ -1148,7 +1154,7 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra return; } } - DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix); + DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix); // only have a "next" if this new layer doesn't affect the clip (rare) layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer; @@ -1209,7 +1215,8 @@ void SkCanvas::internalRestore() { if (fMCRec) { const SkIPoint& origin = layer->fDevice->getOrigin(); this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(), - layer->fPaint.get()); + layer->fPaint.get(), + layer->fClipImage.get(), layer->fClipMatrix); // restore what we smashed in internalSaveLayer fMCRec->fMatrix = layer->fStashedMatrix; // reset this, since internalDrawDevice will have set it to true @@ -1302,7 +1309,8 @@ bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) { ///////////////////////////////////////////////////////////////////////////// -void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) { +void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint, + SkImage* clipImage, const SkMatrix& clipMatrix) { SkPaint tmp; if (nullptr == paint) { paint = &tmp; @@ -1315,10 +1323,11 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPa paint = &looper.paint(); SkImageFilter* filter = paint->getImageFilter(); SkIPoint pos = { x - iter.getX(), y - iter.getY() }; - if (filter) { + if (filter || clipImage) { sk_sp specialImage = srcDev->snapSpecial(); if (specialImage) { - dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint); + dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint, + clipImage, clipMatrix); } } else { dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint); @@ -2219,7 +2228,8 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S iter.fDevice->ctm().mapXY(x, y, &pt); iter.fDevice->drawSpecial(special.get(), SkScalarRoundToInt(pt.fX), - SkScalarRoundToInt(pt.fY), pnt); + SkScalarRoundToInt(pt.fY), pnt, + nullptr, SkMatrix::I()); } else { iter.fDevice->drawImage(image, x, y, pnt); } @@ -2300,7 +2310,8 @@ void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, cons iter.fDevice->ctm().mapXY(x, y, &pt); iter.fDevice->drawSpecial(special.get(), SkScalarRoundToInt(pt.fX), - SkScalarRoundToInt(pt.fY), pnt); + SkScalarRoundToInt(pt.fY), pnt, + nullptr, SkMatrix::I()); } else { iter.fDevice->drawBitmap(bitmap, matrix, looper.paint()); } diff --git a/src/core/SkColorSpaceXformCanvas.cpp b/src/core/SkColorSpaceXformCanvas.cpp index f026c6d..b4dd5bb 100644 --- a/src/core/SkColorSpaceXformCanvas.cpp +++ b/src/core/SkColorSpaceXformCanvas.cpp @@ -229,10 +229,13 @@ public: SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override { sk_sp backdrop = rec.fBackdrop ? fXformer->apply(rec.fBackdrop) : nullptr; + sk_sp clipMask = rec.fClipMask ? fXformer->apply(rec.fClipMask.get()) : nullptr; fTarget->saveLayer({ rec.fBounds, MaybePaint(rec.fPaint, fXformer.get()), backdrop.get(), + std::move(clipMask), + rec.fClipMatrix, rec.fSaveLayerFlags, }); return kNoLayer_SaveLayerStrategy; diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index f608134..0b72eeb 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -282,7 +282,8 @@ void SkBaseDevice::drawAtlas(const SkImage* atlas, const SkRSXform xform[], /////////////////////////////////////////////////////////////////////////////////////////////////// -void SkBaseDevice::drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) {} +void SkBaseDevice::drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&, + SkImage*, const SkMatrix&) {} sk_sp SkBaseDevice::makeSpecial(const SkBitmap&) { return nullptr; } sk_sp SkBaseDevice::makeSpecial(const SkImage*) { return nullptr; } sk_sp SkBaseDevice::snapSpecial() { return nullptr; } diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h index f1f5488..17ccfdd 100644 --- a/src/core/SkDevice.h +++ b/src/core/SkDevice.h @@ -258,7 +258,8 @@ protected: virtual void drawTextRSXform(const void* text, size_t len, const SkRSXform[], const SkPaint&); - virtual void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&); + virtual void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&, + SkImage* clipImage, const SkMatrix& clipMatrix); virtual sk_sp makeSpecial(const SkBitmap&); virtual sk_sp makeSpecial(const SkImage*); virtual sk_sp snapSpecial(); diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h index 3168735..b142e7e 100644 --- a/src/core/SkDraw.h +++ b/src/core/SkDraw.h @@ -96,6 +96,8 @@ public: SkMask* mask, SkMask::CreateMode mode, SkStrokeRec::InitStyle style); + void drawDevMask(const SkMask& mask, const SkPaint&) const; + enum RectType { kHair_RectType, kFill_RectType, @@ -122,7 +124,6 @@ public: const SkPaint&, const SkSurfaceProps*) const; static SkScalar ComputeResScaleForStroking(const SkMatrix& ); private: - void drawDevMask(const SkMask& mask, const SkPaint&) const; void drawBitmapAsMask(const SkBitmap&, const SkPaint&) const; void drawPath(const SkPath&, const SkPaint&, const SkMatrix* preMatrix, diff --git a/src/core/SkLiteDL.cpp b/src/core/SkLiteDL.cpp index 4a8280d..f840607 100644 --- a/src/core/SkLiteDL.cpp +++ b/src/core/SkLiteDL.cpp @@ -91,18 +91,24 @@ namespace { struct SaveLayer final : Op { static const auto kType = Type::SaveLayer; SaveLayer(const SkRect* bounds, const SkPaint* paint, - const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) { + const SkImageFilter* backdrop, sk_sp clipMask, + const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) { if (bounds) { this->bounds = *bounds; } if (paint) { this->paint = *paint; } this->backdrop = sk_ref_sp(backdrop); + this->clipMask = std::move(clipMask); + this->clipMatrix = clipMatrix ? *clipMatrix : SkMatrix::I(); this->flags = flags; } SkRect bounds = kUnset; SkPaint paint; sk_sp backdrop; + sk_sp clipMask; + SkMatrix clipMatrix; SkCanvas::SaveLayerFlags flags; void draw(SkCanvas* c, const SkMatrix&) const { - c->saveLayer({ maybe_unset(bounds), &paint, backdrop.get(), flags }); + c->saveLayer({ maybe_unset(bounds), &paint, backdrop.get(), clipMask, + clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags }); } }; @@ -544,8 +550,9 @@ void SkLiteDL::setDrawFilter(SkDrawFilter* df) { void SkLiteDL:: save() { this->push (0); } void SkLiteDL::restore() { this->push(0); } void SkLiteDL::saveLayer(const SkRect* bounds, const SkPaint* paint, - const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) { - this->push(0, bounds, paint, backdrop, flags); + const SkImageFilter* backdrop, sk_sp clipMask, + const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) { + this->push(0, bounds, paint, backdrop, std::move(clipMask), clipMatrix, flags); } void SkLiteDL:: concat(const SkMatrix& matrix) { this->push (0, matrix); } diff --git a/src/core/SkLiteDL.h b/src/core/SkLiteDL.h index 1f344cd..5219cd0 100644 --- a/src/core/SkLiteDL.h +++ b/src/core/SkLiteDL.h @@ -29,7 +29,8 @@ public: #endif void save(); - void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, SkCanvas::SaveLayerFlags); + void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, sk_sp, + const SkMatrix*, SkCanvas::SaveLayerFlags); void restore(); void concat (const SkMatrix&); diff --git a/src/core/SkLiteRecorder.cpp b/src/core/SkLiteRecorder.cpp index f899f88..c58355b 100644 --- a/src/core/SkLiteRecorder.cpp +++ b/src/core/SkLiteRecorder.cpp @@ -31,7 +31,8 @@ SkDrawFilter* SkLiteRecorder::setDrawFilter(SkDrawFilter* df) { void SkLiteRecorder::willSave() { fDL->save(); } SkCanvas::SaveLayerStrategy SkLiteRecorder::getSaveLayerStrategy(const SaveLayerRec& rec) { - fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fSaveLayerFlags); + fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fClipMask, rec.fClipMatrix, + rec.fSaveLayerFlags); return SkCanvas::kNoLayer_SaveLayerStrategy; } void SkLiteRecorder::willRestore() { fDL->restore(); } diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h index a164746..745a7e3 100644 --- a/src/core/SkPictureFlat.h +++ b/src/core/SkPictureFlat.h @@ -122,6 +122,8 @@ enum SaveLayerRecFlatFlags { SAVELAYERREC_HAS_PAINT = 1 << 1, SAVELAYERREC_HAS_BACKDROP = 1 << 2, SAVELAYERREC_HAS_FLAGS = 1 << 3, + SAVELAYERREC_HAS_CLIPMASK = 1 << 4, + SAVELAYERREC_HAS_CLIPMATRIX = 1 << 5, }; /////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index 51a3da9..84228d0 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -724,7 +724,8 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader, canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, flags)); } break; case SAVE_LAYER_SAVELAYERREC: { - SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, 0); + SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, nullptr, nullptr, 0); + SkMatrix clipMatrix; const uint32_t flatFlags = reader->readInt(); SkRect bounds; if (flatFlags & SAVELAYERREC_HAS_BOUNDS) { @@ -742,6 +743,13 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader, if (flatFlags & SAVELAYERREC_HAS_FLAGS) { rec.fSaveLayerFlags = reader->readInt(); } + if (flatFlags & SAVELAYERREC_HAS_CLIPMASK) { + rec.fClipMask = sk_ref_sp(const_cast(fPictureData->getImage(reader))); + } + if (flatFlags & SAVELAYERREC_HAS_CLIPMATRIX) { + reader->readMatrix(&clipMatrix); + rec.fClipMatrix = &clipMatrix; + } BREAK_ON_READ_ERROR(reader); canvas->saveLayer(rec); diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index 9bdb6c5..ea4273f 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -98,6 +98,14 @@ void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) { flatFlags |= SAVELAYERREC_HAS_FLAGS; size += sizeof(uint32_t); } + if (rec.fClipMask) { + flatFlags |= SAVELAYERREC_HAS_CLIPMASK; + size += sizeof(uint32_t); // clip image index + } + if (rec.fClipMatrix) { + flatFlags |= SAVELAYERREC_HAS_CLIPMATRIX; + size += rec.fClipMatrix->writeToMemory(nullptr); + } const size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERREC, &size); this->addInt(flatFlags); @@ -116,6 +124,12 @@ void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) { if (flatFlags & SAVELAYERREC_HAS_FLAGS) { this->addInt(rec.fSaveLayerFlags); } + if (flatFlags & SAVELAYERREC_HAS_CLIPMASK) { + this->addImage(rec.fClipMask.get()); + } + if (flatFlags & SAVELAYERREC_HAS_CLIPMATRIX) { + this->addMatrix(*rec.fClipMatrix); + } this->validate(initialOffset, size); } diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index 21f9de9..02c8c69 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -77,6 +77,8 @@ DRAW(Save, save()); DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds, r.paint, r.backdrop.get(), + r.clipMask, + r.clipMatrix, r.saveLayerFlags))); DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix))); DRAW(Concat, concat(r.matrix)); diff --git a/src/core/SkRecordOpts.cpp b/src/core/SkRecordOpts.cpp index 07ef19d..8a1ac59 100644 --- a/src/core/SkRecordOpts.cpp +++ b/src/core/SkRecordOpts.cpp @@ -188,8 +188,8 @@ struct SaveLayerDrawRestoreNooper { typedef Pattern, IsDraw, Is> Match; bool onMatch(SkRecord* record, Match* match, int begin, int end) { - if (match->first()->backdrop) { - // can't throw away the layer if we have a backdrop + if (match->first()->backdrop || match->first()->clipMask) { + // can't throw away the layer if we have a backdrop or clip mask return false; } diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp index 0f2891a..c9d88fc 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -366,6 +366,8 @@ SkCanvas::SaveLayerStrategy SkRecorder::getSaveLayerStrategy(const SaveLayerRec& APPEND(SaveLayer, this->copy(rec.fBounds) , this->copy(rec.fPaint) , sk_ref_sp(rec.fBackdrop) + , rec.fClipMask + , this->copy(rec.fClipMatrix) , rec.fSaveLayerFlags); return SkCanvas::kNoLayer_SaveLayerStrategy; } diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 2d3178c..19d9185 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1125,17 +1125,18 @@ void SkGpuDevice::drawSprite(const SkBitmap& bitmap, return; } - this->drawSpecial(srcImg.get(), left, top, paint); + this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I()); } -void SkGpuDevice::drawSpecial(SkSpecialImage* special1, - int left, int top, - const SkPaint& paint) { +void SkGpuDevice::drawSpecial(SkSpecialImage* special1, int left, int top, const SkPaint& paint, + SkImage* clipImage,const SkMatrix& clipMatrix) { ASSERT_SINGLE_OWNER CHECK_SHOULD_DRAW(); GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSpecial", fContext.get()); + // TODO: clipImage support. + SkIPoint offset = { 0, 0 }; sk_sp result; @@ -1354,7 +1355,7 @@ void SkGpuDevice::drawDevice(SkBaseDevice* device, return; } - this->drawSpecial(srcImg.get(), left, top, paint); + this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I()); } void SkGpuDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y, diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h index 4558c1b..340f39a 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -110,8 +110,8 @@ public: void drawBitmapLattice(const SkBitmap&, const SkCanvas::Lattice&, const SkRect& dst, const SkPaint&) override; - void drawSpecial(SkSpecialImage*, - int left, int top, const SkPaint& paint) override; + void drawSpecial(SkSpecialImage*, int left, int top, const SkPaint& paint, + SkImage*, const SkMatrix&) override; sk_sp makeSpecial(const SkBitmap&) override; sk_sp makeSpecial(const SkImage*) override; sk_sp snapSpecial() override; diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 768277a..aa8f7ae 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -2202,10 +2202,12 @@ void SkPDFDevice::internalDrawImage(const SkMatrix& origMatrix, #include "SkSpecialImage.h" #include "SkImageFilter.h" -void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y, - const SkPaint& paint) { +void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y, const SkPaint& paint, + SkImage* clipImage, const SkMatrix& clipMatrix) { SkASSERT(!srcImg->isTextureBacked()); + //TODO: clipImage support + SkBitmap resultBM; SkImageFilter* filter = paint.getImageFilter(); diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h index 93eb78d..f4318e4 100644 --- a/src/pdf/SkPDFDevice.h +++ b/src/pdf/SkPDFDevice.h @@ -170,7 +170,8 @@ protected: void drawAnnotation(const SkRect&, const char key[], SkData* value) override; - void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override; + void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&, + SkImage*, const SkMatrix&) override; sk_sp makeSpecial(const SkBitmap&) override; sk_sp makeSpecial(const SkImage*) override; sk_sp snapSpecial() override; diff --git a/src/pipe/SkPipeCanvas.cpp b/src/pipe/SkPipeCanvas.cpp index 1473b84..91e74ee 100644 --- a/src/pipe/SkPipeCanvas.cpp +++ b/src/pipe/SkPipeCanvas.cpp @@ -240,6 +240,12 @@ SkCanvas::SaveLayerStrategy SkPipeCanvas::getSaveLayerStrategy(const SaveLayerRe if (rec.fBackdrop) { extra |= kHasBackdrop_SaveLayerMask; } + if (rec.fClipMask) { + extra |= kHasClipMask_SaveLayerMask; + } + if (rec.fClipMatrix) { + extra |= kHasClipMatrix_SaveLayerMask; + } writer.write32(pack_verb(SkPipeVerb::kSaveLayer, extra)); if (rec.fBounds) { @@ -251,6 +257,13 @@ SkCanvas::SaveLayerStrategy SkPipeCanvas::getSaveLayerStrategy(const SaveLayerRe if (rec.fBackdrop) { writer.writeFlattenable(rec.fBackdrop); } + if (rec.fClipMask) { + writer.writeImage(rec.fClipMask.get()); + } + if (rec.fClipMatrix) { + writer.writeMatrix(*rec.fClipMatrix); + } + return kNoLayer_SaveLayerStrategy; } diff --git a/src/pipe/SkPipeFormat.h b/src/pipe/SkPipeFormat.h index 9a1d30c..a652127 100644 --- a/src/pipe/SkPipeFormat.h +++ b/src/pipe/SkPipeFormat.h @@ -107,6 +107,8 @@ enum { kHasPaint_SaveLayerMask = 1 << 9, kHasBackdrop_SaveLayerMask = 1 << 10, kDontClipToLayer_SaveLayerMask = 1 << 11, + kHasClipMask_SaveLayerMask = 1 << 12, + kHasClipMatrix_SaveLayerMask = 1 << 13, }; enum { diff --git a/src/pipe/SkPipeReader.cpp b/src/pipe/SkPipeReader.cpp index 974e6b0..73ef970 100644 --- a/src/pipe/SkPipeReader.cpp +++ b/src/pipe/SkPipeReader.cpp @@ -244,6 +244,14 @@ static void saveLayer_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanva if (extra & kHasBackdrop_SaveLayerMask) { backdrop = reader.readImageFilter(); } + sk_sp clipMask; + if (extra & kHasClipMask_SaveLayerMask) { + clipMask = reader.readImage(); + } + SkMatrix clipMatrix; + if (extra & kHasClipMatrix_SaveLayerMask) { + reader.readMatrix(&clipMatrix); + } SkCanvas::SaveLayerFlags flags = (SkCanvas::SaveLayerFlags)(extra & kFlags_SaveLayerMask); // unremap this wacky flag @@ -251,7 +259,8 @@ static void saveLayer_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanva flags |= (1 << 31);//SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag; } - canvas->saveLayer(SkCanvas::SaveLayerRec(bounds, paint, backdrop.get(), flags)); + canvas->saveLayer(SkCanvas::SaveLayerRec(bounds, paint, backdrop.get(), std::move(clipMask), + (extra & kHasClipMatrix_SaveLayerMask) ? &clipMatrix : nullptr, flags)); } static void restore_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanvas* canvas) { diff --git a/tests/RecordOptsTest.cpp b/tests/RecordOptsTest.cpp index 09e774e..d34959a 100644 --- a/tests/RecordOptsTest.cpp +++ b/tests/RecordOptsTest.cpp @@ -190,10 +190,19 @@ DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore, r) { // saveLayer w/ backdrop should NOT go away sk_sp filter(SkBlurImageFilter::Make(3, 3, nullptr)); - recorder.saveLayer({ nullptr, nullptr, filter.get(), 0}); + recorder.saveLayer({ nullptr, nullptr, filter.get(), nullptr, nullptr, 0}); recorder.drawRect(draw, opaqueDrawPaint); recorder.restore(); assert_savelayer_draw_restore(r, &record, 18, false); + + // saveLayer w/ clip mask should also NOT go away + { + sk_sp surface(SkSurface::MakeRasterN32Premul(10, 10)); + recorder.saveLayer({ nullptr, nullptr, nullptr, surface->makeImageSnapshot(), nullptr, 0}); + recorder.drawRect(draw, opaqueDrawPaint); + recorder.restore(); + assert_savelayer_draw_restore(r, &record, 21, false); + } } #endif @@ -265,10 +274,12 @@ DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) { for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) { bool innerNoOped = !secondBounds[k] && !secondPaints[m] && !innerF; - recorder.saveLayer({firstBounds[i], firstPaints[j], outerF, 0}); + recorder.saveLayer({firstBounds[i], firstPaints[j], outerF, + nullptr, nullptr, 0}); recorder.save(); recorder.clipRect(clip); - recorder.saveLayer({secondBounds[k], secondPaints[m], innerF, 0}); + recorder.saveLayer({secondBounds[k], secondPaints[m], innerF, + nullptr, nullptr, 0}); recorder.restore(); recorder.restore(); recorder.restore();