aboutsummaryrefslogtreecommitdiff
path: root/src/core/SkCanvas.cpp
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2019-04-04 11:49:14 -0400
committerMichele Bono <bono.michele94@gmail.com>2019-08-07 16:13:50 +0200
commit55c3d512b95a8b515b7a6a8542da8c0f28270b2e (patch)
treea6098e8aaafb8daeed174c4c9c784c5cb26e6689 /src/core/SkCanvas.cpp
parent1f50aa929088085c6a4fa1c6de435193cbde9ee2 (diff)
Add private save-behind and draw-behind methods to canvas.HEADp9.0
The draw-behind is a variant of drawPaint but is automatically clipped to the bounds of the most recent saveBehind buffer (axis-aligned bounds). No public exposure outside of the Android framework. Impl is pretty simple (its a variant of drawPaint) - find the most recent saveBehind device bounds - if there is none, draw nothing, else - temporarily intersect the device's clip with that bounds - drawPaint - restore the clip This patches did not apply cleanly and have been updated to compile with a previous version of Skia. It was cherry-picked from the following 3 Skia commits: 148b7fd3ad9c29dec0052de624c26ff291ef8f0a d567408362bf7847d6000f6786f9a7b2c9d0b88b 9adc82c73df0ef25b708cae8aa48ef9c39ed4c67 Bug: 129117085 Test: None Change-Id: I291f57885de6e95f749bf5cdb70ac16a5781ffb1
Diffstat (limited to 'src/core/SkCanvas.cpp')
-rw-r--r--src/core/SkCanvas.cpp118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index b29a538717..75aff1c463 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -272,6 +272,14 @@ struct DeviceCM {
}
};
+namespace {
+// Encapsulate state needed to restore from saveBehind()
+struct BackImage {
+ sk_sp<SkSpecialImage> fImage;
+ SkIPoint fLoc;
+};
+}
+
/* This is the record we keep for each save/restore level in the stack.
Since a level optionally copies the matrix and/or stack, we have pointers
for these fields. If the value is copied for this level, the copy is
@@ -290,6 +298,7 @@ public:
or a previous one in a lower level.)
*/
DeviceCM* fTopLayer;
+ std::unique_ptr<BackImage> fBackImage;
SkConservativeClip fRasterClip;
SkMatrix fMatrix;
int fDeferredSaveCount;
@@ -1011,6 +1020,22 @@ int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
return this->getSaveCount() - 1;
}
+int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
+ if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
+ // Assuming clips never expand, if the request bounds is outside of the current clip
+ // there is no need to copy/restore the area, so just devolve back to a regular save.
+ this->save();
+ } else {
+ bool doTheWork = this->onDoSaveBehind(bounds);
+ fSaveCount += 1;
+ this->internalSave();
+ if (doTheWork) {
+ this->internalSaveBehind(bounds);
+ }
+ }
+ return this->getSaveCount() - 1;
+}
+
void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
SkBaseDevice* dst, const SkIPoint& dstOrigin,
const SkMatrix& ctm) {
@@ -1171,6 +1196,44 @@ int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
}
}
+void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
+ SkIRect devBounds;
+ if (localBounds) {
+ SkRect tmp;
+ fMCRec->fMatrix.mapRect(&tmp, *localBounds);
+ if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
+ devBounds.setEmpty();
+ }
+ } else {
+ devBounds = this->getDeviceClipBounds();
+ }
+ if (devBounds.isEmpty()) {
+ return;
+ }
+
+ SkBaseDevice* device = this->getTopDevice();
+ if (nullptr == device) { // Do we still need this check???
+ return;
+ }
+
+ // need the bounds relative to the device itself
+ devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
+
+ auto backImage = device->snapBackImage(devBounds);
+ if (!backImage) {
+ return;
+ }
+
+ // we really need the save, so we can wack the fMCRec
+ this->checkForDeferredSave();
+
+ fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kClear);
+ this->drawClippedToSaveBehind(paint);
+}
+
void SkCanvas::internalRestore() {
SkASSERT(fMCStack.count() != 0);
@@ -1179,6 +1242,9 @@ void SkCanvas::internalRestore() {
// now detach it from fMCRec so we can pop(). Gets freed after its drawn
fMCRec->fLayer = nullptr;
+ // move this out before we do the actual restore
+ auto backImage = std::move(fMCRec->fBackImage);
+
// now do the normal restore()
fMCRec->~MCRec(); // balanced in save()
fMCStack.pop_back();
@@ -1188,6 +1254,15 @@ void SkCanvas::internalRestore() {
FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
}
+ if (backImage) {
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kDstOver);
+ const int x = backImage->fLoc.x();
+ const int y = backImage->fLoc.y();
+ this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
+ nullptr, SkMatrix::I());
+ }
+
/* Time to draw the layer's offscreen. We can't call the public drawSprite,
since if we're being recorded, we don't want to record this (the
recorder will have already recorded the restore).
@@ -1710,6 +1785,11 @@ void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
this->onDrawRect(r.makeSorted(), paint);
}
+void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
+ TRACE_EVENT0("skia", TRACE_FUNC);
+ this->onDrawBehind(paint);
+}
+
void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
TRACE_EVENT0("skia", TRACE_FUNC);
if (region.isEmpty()) {
@@ -2086,6 +2166,40 @@ void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
LOOPER_END
}
+void SkCanvas::onDrawBehind(const SkPaint& paint) {
+ SkIRect bounds;
+ SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
+ for (;;) {
+ const MCRec* rec = (const MCRec*)iter.prev();
+ if (!rec) {
+ return; // no backimages, so nothing to draw
+ }
+ if (rec->fBackImage) {
+ bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
+ rec->fBackImage->fImage->width(),
+ rec->fBackImage->fImage->height());
+ break;
+ }
+ }
+
+ LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, nullptr)
+
+ while (iter.next()) {
+ SkBaseDevice* dev = iter.fDevice;
+
+ dev->save();
+ // We use clipRegion because it is already defined to operate in dev-space
+ // (i.e. ignores the ctm). However, it is going to first translate by -origin,
+ // but we don't want that, so we undo that before calling in.
+ SkRegion rgn(bounds.makeOffset(dev->fOrigin.fX, dev->fOrigin.fY));
+ dev->clipRegion(rgn, SkClipOp::kIntersect);
+ dev->drawPaint(looper.paint());
+ dev->restore(fMCRec->fMatrix);
+ }
+
+ LOOPER_END
+}
+
void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
SkASSERT(oval.isSorted());
if (paint.canComputeFastBounds()) {
@@ -2960,6 +3074,10 @@ SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayer
return kNoLayer_SaveLayerStrategy;
}
+bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
+ return false;
+}
+
///////////////////////////////////////////////////////////////////////////////
static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");