Move inval dedup to Window for wider usages.

It turns out that the inval dedup is not just useful for Android.  Hence
we move it up to the Window level so more OSes such as Linux, Windows
can also use it.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2001153002

Review-Url: https://codereview.chromium.org/2001153002
diff --git a/tools/viewer/sk_app/Window.cpp b/tools/viewer/sk_app/Window.cpp
index 0a7bcf8..b78f39a 100644
--- a/tools/viewer/sk_app/Window.cpp
+++ b/tools/viewer/sk_app/Window.cpp
@@ -63,6 +63,7 @@
 }
 
 void Window::onPaint() {
+    markInvalProcessed();
     sk_sp<SkSurface> backbuffer = fWindowContext->getBackbufferSurface();
     if (backbuffer) {
         // draw into the canvas of this surface
@@ -92,4 +93,15 @@
     fWindowContext->setDisplayParams(params);
 }
 
+void Window::inval() {
+    if (!fIsContentInvalidated) {
+        fIsContentInvalidated = true;
+        onInval();
+    }
+}
+
+void Window::markInvalProcessed() {
+    fIsContentInvalidated = false;
+}
+
 }   // namespace sk_app
diff --git a/tools/viewer/sk_app/Window.h b/tools/viewer/sk_app/Window.h
index 63d5e19..65e1542 100644
--- a/tools/viewer/sk_app/Window.h
+++ b/tools/viewer/sk_app/Window.h
@@ -27,7 +27,11 @@
 
     virtual void setTitle(const char*) = 0;
     virtual void show() = 0;
-    virtual void inval() = 0;
+
+    // Shedules an invalidation event for window if one is not currently pending.
+    // Make sure that either onPaint or markInvalReceived is called when the client window consumes
+    // the the inval event. They unset fIsContentInvalided which allow future onInval.
+    void inval();
 
     virtual bool scaleContentToFit() const { return false; }
     virtual bool supportsContentRect() const { return false; }
@@ -164,6 +168,13 @@
     void*        fPaintUserData;
 
     WindowContext* fWindowContext = nullptr;
+
+    virtual void onInval() = 0;
+
+    // Uncheck fIsContentInvalided to allow future inval/onInval.
+    void markInvalProcessed();
+
+    bool fIsContentInvalidated = false;  // use this to avoid duplicate invalidate events
 };
 
 }   // namespace sk_app
diff --git a/tools/viewer/sk_app/android/Window_android.cpp b/tools/viewer/sk_app/android/Window_android.cpp
index 106c40b..ed03c81 100644
--- a/tools/viewer/sk_app/android/Window_android.cpp
+++ b/tools/viewer/sk_app/android/Window_android.cpp
@@ -64,8 +64,16 @@
     detach();
 }
 
-void Window_android::inval() {
-    fSkiaAndroidApp->inval();
+void Window_android::onInval() {
+    fSkiaAndroidApp->postMessage(Message(kContentInvalidated));
+}
+
+void Window_android::paintIfNeeded() {
+    if (fWindowContext) { // Check if initDisplay has already been called
+        onPaint();
+    } else {
+        markInvalProcessed();
+    }
 }
 
 }   // namespace sk_app
diff --git a/tools/viewer/sk_app/android/Window_android.h b/tools/viewer/sk_app/android/Window_android.h
index c1cd1eb..f7d348b 100644
--- a/tools/viewer/sk_app/android/Window_android.h
+++ b/tools/viewer/sk_app/android/Window_android.h
@@ -27,7 +27,9 @@
     void show() override {}
 
     bool attach(BackendType attachType, const DisplayParams& params) override;
-    void inval() override;
+    void onInval() override;
+
+    void paintIfNeeded();
 
     bool scaleContentToFit() const override { return true; }
     bool supportsContentRect() const override { return true; }
diff --git a/tools/viewer/sk_app/android/surface_glue_android.cpp b/tools/viewer/sk_app/android/surface_glue_android.cpp
index 3a4fca5..958b787 100644
--- a/tools/viewer/sk_app/android/surface_glue_android.cpp
+++ b/tools/viewer/sk_app/android/surface_glue_android.cpp
@@ -70,12 +70,6 @@
     fPThreadEnv->DeleteLocalRef(titleString);
 }
 
-void SkiaAndroidApp::paintIfNeeded() {
-    if (fNativeWindow && fWindow) {
-        fWindow->onPaint();
-    }
-}
-
 void SkiaAndroidApp::postMessage(const Message& message) const {
     SkDEBUGCODE(auto writeSize =) write(fPipes[1], &message, sizeof(message));
     SkASSERT(writeSize == sizeof(message));
@@ -86,14 +80,6 @@
     SkASSERT(readSize == sizeof(Message));
 }
 
-void SkiaAndroidApp::inval() {
-    SkAutoMutexAcquire ama(fMutex);
-    if (!fIsContentInvalidated) {
-        postMessage(Message(kContentInvalidated));
-        fIsContentInvalidated = true;
-    }
-}
-
 int SkiaAndroidApp::message_callback(int fd, int events, void* data) {
     auto skiaAndroidApp = (SkiaAndroidApp*)data;
     Message message;
@@ -108,9 +94,7 @@
             return 0;
         }
         case kContentInvalidated: {
-            SkAutoMutexAcquire ama(skiaAndroidApp->fMutex);
-            skiaAndroidApp->fIsContentInvalidated = false;
-            skiaAndroidApp->paintIfNeeded();
+            ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
             break;
         }
         case kSurfaceCreated: {
@@ -118,7 +102,7 @@
             skiaAndroidApp->fNativeWindow = message.fNativeWindow;
             auto window_android = (Window_android*)skiaAndroidApp->fWindow;
             window_android->initDisplay(skiaAndroidApp->fNativeWindow);
-            skiaAndroidApp->paintIfNeeded();
+            ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
             break;
         }
         case kSurfaceChanged: {
@@ -128,7 +112,7 @@
             int height = ANativeWindow_getHeight(skiaAndroidApp->fNativeWindow);
             auto window_android = (Window_android*)skiaAndroidApp->fWindow;
             window_android->setContentRect(0, 0, width, height);
-            skiaAndroidApp->paintIfNeeded();
+            window_android->paintIfNeeded();
             break;
         }
         case kSurfaceDestroyed: {
diff --git a/tools/viewer/sk_app/android/surface_glue_android.h b/tools/viewer/sk_app/android/surface_glue_android.h
index 7ffba30..a469833 100644
--- a/tools/viewer/sk_app/android/surface_glue_android.h
+++ b/tools/viewer/sk_app/android/surface_glue_android.h
@@ -12,7 +12,6 @@
 
 #include <android/native_window_jni.h>
 
-#include "../private/SkMutex.h"
 #include "../Application.h"
 #include "../Window.h"
 
@@ -49,14 +48,10 @@
 
     void postMessage(const Message& message) const;
     void readMessage(Message* message) const;
-    void paintIfNeeded();
 
     // This must be called in SkiaAndroidApp's own pthread because the JNIEnv is thread sensitive
     void setTitle(const char* title) const;
 
-    // This posts a kContentInvalidated message if there's no such message currently in the queue
-    void inval();
-
 private:
     pthread_t fThread;
     ANativeWindow* fNativeWindow;
@@ -65,9 +60,6 @@
     JNIEnv* fPThreadEnv;
     jmethodID fSetTitleMethodID;
 
-    bool fIsContentInvalidated = false;  // use this to avoid duplicate invalidate events
-    SkMutex fMutex;
-
     // This must be called in SkiaAndroidApp's own pthread because the JNIEnv is thread sensitive
     ~SkiaAndroidApp();
 
diff --git a/tools/viewer/sk_app/win/Window_win.cpp b/tools/viewer/sk_app/win/Window_win.cpp
index 8355c72..f9762a8 100644
--- a/tools/viewer/sk_app/win/Window_win.cpp
+++ b/tools/viewer/sk_app/win/Window_win.cpp
@@ -284,7 +284,7 @@
     return (SkToBool(fWindowContext));
 }
 
-void Window_win::inval() {
+void Window_win::onInval() {
     InvalidateRect(fHWnd, nullptr, false);
 }
 
diff --git a/tools/viewer/sk_app/win/Window_win.h b/tools/viewer/sk_app/win/Window_win.h
index 4dd829a..36c0505 100644
--- a/tools/viewer/sk_app/win/Window_win.h
+++ b/tools/viewer/sk_app/win/Window_win.h
@@ -31,7 +31,7 @@
 
     bool attach(BackendType attachType, const DisplayParams& params) override;
 
-    void inval() override;
+    void onInval() override;
 
 private:
     HINSTANCE fHInstance;