Index: src/core/SkCanvas.cpp |
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp |
index 35551e7e9a1e88b64537369c190fbcfd4d0b9ec2..c0db416cbbddf100eb83544818a2bc01c11a4688 100644 |
--- a/src/core/SkCanvas.cpp |
+++ b/src/core/SkCanvas.cpp |
@@ -17,6 +17,7 @@ |
#include "SkImage.h" |
#include "SkMetaData.h" |
#include "SkNinePatchIter.h" |
+#include "SkPaintPriv.h" |
#include "SkPathOps.h" |
#include "SkPatchUtils.h" |
#include "SkPicture.h" |
@@ -36,6 +37,51 @@ |
#include "GrRenderTarget.h" |
#endif |
+/* |
+ * Return true if the drawing this rect would hit every pixels in the canvas. |
+ * |
+ * Returns false if |
+ * - rect does not contain the canvas' bounds |
+ * - paint is not fill |
+ * - paint would blur or otherwise change the coverage of the rect |
+ */ |
+bool SkCanvas::willOverwriteAllPixels(const SkRect* rect, const SkPaint* paint, |
Justin Novosad
2015/07/15 18:00:46
"will" is ambiguous and can be interpreted as a de
reed1
2015/07/15 21:41:02
Done.
|
+ bool auxOpaque) const { |
+ const SkISize size = this->getBaseLayerSize(); |
+ const SkRect bounds = SkRect::MakeIWH(size.width(), size.height()); |
+ if (!this->getClipStack()->quickContains(bounds)) { |
+ return false; |
+ } |
+ |
+ if (rect) { |
+ if (!this->getTotalMatrix().rectStaysRect()) { |
+ return false; // conservative |
+ } |
+ |
+ SkRect devRect; |
+ this->getTotalMatrix().mapRect(&devRect, *rect); |
+ |
+ if (paint) { |
+ SkPaint::Style paintStyle = paint->getStyle(); |
+ if (!(paintStyle == SkPaint::kFill_Style || |
+ paintStyle == SkPaint::kStrokeAndFill_Style)) { |
+ return false; |
+ } |
+ if (paint->getMaskFilter() || paint->getLooper() |
+ || paint->getPathEffect() || paint->getImageFilter()) { |
+ return false; // conservative |
+ } |
+ } |
+ |
+ if (devRect.contains(bounds)) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
static bool gIgnoreSaveLayerBounds; |
void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) { |
gIgnoreSaveLayerBounds = ignore; |
@@ -80,9 +126,26 @@ bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() { |
typedef SkTLazy<SkPaint> SkLazyPaint; |
-void SkCanvas::predrawNotify() { |
+void SkCanvas::predrawNotify(bool completeOverwrite) { |
+ if (fSurfaceBase) { |
+ fSurfaceBase->aboutToDraw(completeOverwrite ? SkSurface::kDiscard_ContentChangeMode |
+ : SkSurface::kRetain_ContentChangeMode); |
+ } |
+} |
+ |
+void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint, bool auxOpaque) { |
if (fSurfaceBase) { |
- fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode); |
+ SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode; |
+ // Since willOverwriteAllPixels() may not be complete free to call, we only do so if |
+ // there is an outstanding snapshot, since w/o that, there will be no copy-on-write |
+ // and therefore we don't care which mode we're in. |
+ // |
+ if (fSurfaceBase->outstandingImageSnapshot()) { |
Justin Novosad
2015/07/15 18:00:46
+1 This is a an interesting optimization
|
+ if (this->willOverwriteAllPixels(rect, paint, auxOpaque)) { |
+ mode = SkSurface::kDiscard_ContentChangeMode; |
+ } |
+ } |
+ fSurfaceBase->aboutToDraw(mode); |
} |
} |
@@ -497,6 +560,12 @@ bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) { |
while (looper.next(type)) { \ |
SkDrawIter iter(this); |
+#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \ |
+ this->predrawNotify(bounds, &paint, auxOpaque); \ |
+ AutoDrawLooper looper(this, fProps, paint, false, bounds); \ |
+ while (looper.next(type)) { \ |
+ SkDrawIter iter(this); |
+ |
#define LOOPER_END } |
//////////////////////////////////////////////////////////////////////////// |
@@ -815,7 +884,8 @@ bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size |
pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel()); |
// Tell our owning surface to bump its generation ID |
- this->predrawNotify(); |
+ const bool completeOverwrite = info.dimensions() == size; |
+ this->predrawNotify(completeOverwrite); |
// The device can assert that the requested area is always contained in its bounds |
return device->writePixels(info, pixels, rowBytes, target.x(), target.y()); |
@@ -1852,7 +1922,7 @@ void SkCanvas::onDrawPaint(const SkPaint& paint) { |
} |
void SkCanvas::internalDrawPaint(const SkPaint& paint) { |
- LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL) |
+ LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, NULL) |
while (iter.next()) { |
iter.fDevice->drawPaint(iter, looper.paint()); |
@@ -1910,7 +1980,7 @@ void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) { |
} |
} |
- LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds) |
+ LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds) |
while (iter.next()) { |
iter.fDevice->drawRect(iter, r, looper.paint()); |