Fixing SkDeferredCanvas::writePixels to trigger appropriate change notifications to SkSurface

BUG=crbug.com/256269
TEST=DeferredCanvas unit test, TestDeferredCanvasWritePixelsToSurface
R=reed@google.com

Review URL: https://codereview.chromium.org/20628005

git-svn-id: http://skia.googlecode.com/svn/trunk@10513 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 6dce383..45fa11f 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -242,6 +242,8 @@
 
     void beginRecording();
     void init();
+    void aboutToDraw();
+    void prepareForImmediatePixelWrite();
 
     DeferredPipeController fPipeController;
     SkGPipeWriter  fPipeWriter;
@@ -343,22 +345,25 @@
     return fPipeController.hasPendingCommands();
 }
 
+void DeferredDevice::aboutToDraw()
+{
+    if (NULL != fNotificationClient) {
+        fNotificationClient->prepareForDraw();
+    }
+    if (fCanDiscardCanvasContents) {
+        if (NULL != fSurface) {
+            fSurface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
+        }
+        fCanDiscardCanvasContents = false;    
+    }
+}
+
 void DeferredDevice::flushPendingCommands(PlaybackMode playbackMode) {
     if (!fPipeController.hasPendingCommands()) {
         return;
     }
     if (playbackMode == kNormal_PlaybackMode) {
-        if (NULL != fNotificationClient) {
-            fNotificationClient->prepareForDraw();
-        }
-        if (fCanDiscardCanvasContents) {
-            if (NULL != fSurface) {
-                // Pre-empt notifyContentChanged(false) calls that will happen
-                // during flush
-                fSurface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
-            }
-            fCanDiscardCanvasContents = false;
-        }
+        aboutToDraw();
     }
     fPipeWriter.flushRecording(true);
     fPipeController.playback(kSilent_PlaybackMode == playbackMode);
@@ -441,6 +446,23 @@
     return immediateDevice()->accessRenderTarget();
 }
 
+void DeferredDevice::prepareForImmediatePixelWrite() {
+    // The purpose of the following code is to make sure commands are flushed, that
+    // aboutToDraw() is called and that notifyContentWillChange is called, without
+    // calling anything redundantly.
+    if (fPipeController.hasPendingCommands()) {
+        this->flushPendingCommands(kNormal_PlaybackMode);
+    } else {
+        bool mustNotifyDirectly = !fCanDiscardCanvasContents;
+        this->aboutToDraw();
+        if (mustNotifyDirectly) {
+            fSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
+        }
+    }
+
+    fImmediateCanvas->flush();
+}
+
 void DeferredDevice::writePixels(const SkBitmap& bitmap,
     int x, int y, SkCanvas::Config8888 config8888) {
 
@@ -453,7 +475,7 @@
         SkCanvas::kNative_Premul_Config8888 != config8888 &&
         kPMColorAlias != config8888) {
         //Special case config: no deferral
-        this->flushPendingCommands(kNormal_PlaybackMode);
+        prepareForImmediatePixelWrite();
         immediateDevice()->writePixels(bitmap, x, y, config8888);
         return;
     }
@@ -461,7 +483,7 @@
     SkPaint paint;
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     if (shouldDrawImmediately(&bitmap, NULL, getBitmapSizeThreshold())) {
-        this->flushPendingCommands(kNormal_PlaybackMode);
+        prepareForImmediatePixelWrite();
         fImmediateCanvas->drawSprite(bitmap, x, y, &paint);
     } else {
         this->recordingCanvas()->drawSprite(bitmap, x, y, &paint);