[androidkit] implement ThreadedSurface backed by WindowSurface

This moves ownership of the SkSurface to ThreadedSurface::fWindowSurface.

When we pass a Message to the SurfaceThread, we will include  a pointer to ThreadedSurface.fWindowSurface so we can call:
getCanvas() to draw the SkPicture
WindowSurface's constructor so it can hold the WindowContext made during the init

To reference WindowSurface in SurfaceThread, we need to make a header for Surface.cpp (added in this cl)

Change-Id: I8c67223eee301a1b6e0d05934e1f8597cf70bc64
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/404918
Commit-Queue: Jorge Betancourt <jmbetancourt@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 30ffbc9..b4ed1cf 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2500,6 +2500,7 @@
         "modules/androidkit/src/RuntimeShaderBuilder.cpp",
         "modules/androidkit/src/Shader.cpp",
         "modules/androidkit/src/Surface.cpp",
+        "modules/androidkit/src/Surface.h",
         "modules/androidkit/src/SurfaceThread.cpp",
         "modules/androidkit/src/SurfaceThread.h",
       ]
diff --git a/modules/androidkit/src/Surface.cpp b/modules/androidkit/src/Surface.cpp
index e9e7e23..b24891a 100644
--- a/modules/androidkit/src/Surface.cpp
+++ b/modules/androidkit/src/Surface.cpp
@@ -4,20 +4,13 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#include "modules/androidkit/src/Surface.h"
 
 #include <android/bitmap.h>
 #include <android/log.h>
-#include <android/native_window_jni.h>
-#include <android/native_window.h>
-#include <jni.h>
 
-#include "include/core/SkPictureRecorder.h"
-#include "include/core/SkRefCnt.h"
-#include "include/core/SkSurface.h"
-#include "include/core/SkTypes.h"
 #include "tools/sk_app/Application.h"
 #include "tools/sk_app/DisplayParams.h"
-#include "tools/sk_app/WindowContext.h"
 #include "tools/sk_app/android/WindowContextFactory_android.h"
 
 namespace sk_app {
@@ -28,58 +21,70 @@
 }
 }
 
-#include "modules/androidkit/src/SurfaceThread.h"
+WindowSurface::WindowSurface(ANativeWindow* win, std::unique_ptr<sk_app::WindowContext> wctx)
+    : fWindow(win)
+    , fWindowContext(std::move(wctx))
+{
+    SkASSERT(fWindow);
+    SkASSERT(fWindowContext);
+
+    fSurface = fWindowContext->getBackbufferSurface();
+}
+
+void WindowSurface::release(JNIEnv* env) {
+    fWindowContext.reset();
+    ANativeWindow_release(fWindow);
+}
+
+SkCanvas* WindowSurface::getCanvas() {
+    if (fSurface) {
+        return fSurface->getCanvas();
+    }
+    return nullptr;
+}
+
+void WindowSurface::flushAndSubmit() {
+    fSurface->flushAndSubmit();
+    fWindowContext->swapBuffers();
+    fSurface = fWindowContext->getBackbufferSurface();
+}
+
+// SkSurface created from being passed an android.view.Surface
+// For now, assume we are always rendering with OpenGL
+// TODO: add option of choose backing
+ThreadedSurface::ThreadedSurface(JNIEnv* env, jobject surface)
+      : fThread(std::make_unique<SurfaceThread>()) {
+    ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
+    fWidth = ANativeWindow_getWidth(window);
+    fHeight = ANativeWindow_getHeight(window);
+
+    Message message(kInitialize);
+    message.fNativeWindow = window;
+    message.fWindowSurface = &fWindowSurface;
+    fThread->postMessage(message);
+}
+
+void ThreadedSurface::release(JNIEnv* env) {
+    Message message(kDestroy);
+    message.fWindowSurface = &fWindowSurface;
+    fThread->postMessage(message);
+    fThread->release();
+}
+
+SkCanvas* ThreadedSurface::getCanvas() {
+    return fRecorder.beginRecording(fWidth,
+                                    fHeight);
+}
+
+void ThreadedSurface::flushAndSubmit() {
+    Message message(kRenderPicture);
+    message.fWindowSurface = &fWindowSurface;
+    message.fPicture = fRecorder.finishRecordingAsPicture().release();
+    fThread->postMessage(message);
+}
 
 namespace {
 
-class Surface : public SkRefCnt {
-public:
-    virtual void release(JNIEnv*) = 0;
-    virtual void flushAndSubmit() = 0;
-    virtual SkCanvas* getCanvas() = 0;
-
-    int width()  const { return fSurface ? fSurface->width()  : 0; }
-    int height() const { return fSurface ? fSurface->height() : 0; }
-
-protected:
-    sk_sp<SkSurface> fSurface;
-};
-
-class WindowSurface final : public Surface {
-public:
-    WindowSurface(ANativeWindow* win, std::unique_ptr<sk_app::WindowContext> wctx)
-        : fWindow(win)
-        , fWindowContext(std::move(wctx))
-    {
-        SkASSERT(fWindow);
-        SkASSERT(fWindowContext);
-
-        fSurface = fWindowContext->getBackbufferSurface();
-    }
-
-private:
-    void release(JNIEnv* env) override {
-        fWindowContext.reset();
-        ANativeWindow_release(fWindow);
-    }
-
-    SkCanvas* getCanvas() override {
-        if (fSurface) {
-            return fSurface->getCanvas();
-        }
-        return nullptr;
-    }
-
-    void flushAndSubmit() override {
-        fSurface->flushAndSubmit();
-        fWindowContext->swapBuffers();
-        fSurface = fWindowContext->getBackbufferSurface();
-    }
-
-    ANativeWindow*                         fWindow;
-    std::unique_ptr<sk_app::WindowContext> fWindowContext;
-};
-
 class BitmapSurface final : public Surface {
 public:
     BitmapSurface(JNIEnv* env, jobject bitmap) {
@@ -152,43 +157,6 @@
     jobject fBitmap;
 };
 
-// SkSurface created from being passed an android.view.Surface
-// For now, assume we are always rendering with OpenGL
-// TODO: add option of choose backing
-class ThreadedSurface final : public Surface {
-public:
-    ThreadedSurface(JNIEnv* env, jobject surface) {
-        fWindow = ANativeWindow_fromSurface(env, surface);
-        Message message(kInitialize);
-        message.fNativeWindow = fWindow;
-    }
-
-private:
-    void release(JNIEnv* env) override {
-        fThread.postMessage(Message(kDestroy));
-        if (fWindow) {
-            ANativeWindow_release(fWindow);
-        }
-       fSurface.reset();
-    }
-
-    SkCanvas* getCanvas() override {
-        return fRecorder.beginRecording(ANativeWindow_getWidth(fWindow),
-                                        ANativeWindow_getHeight(fWindow));
-    }
-
-    void flushAndSubmit() override{
-        Message message(kRenderPicture);
-        message.fNativeWindow = fWindow;
-        message.fPicture = fRecorder.finishRecordingAsPicture().release();
-        fThread.postMessage(message);
-    }
-
-    ANativeWindow* fWindow;
-    SkPictureRecorder fRecorder;
-    SurfaceThread fThread;
-};
-
 // *** JNI methods ***
 
 static jlong Surface_CreateBitmap(JNIEnv* env, jobject, jobject bitmap) {
diff --git a/modules/androidkit/src/Surface.h b/modules/androidkit/src/Surface.h
new file mode 100644
index 0000000..7162087
--- /dev/null
+++ b/modules/androidkit/src/Surface.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef AndroidKit_Surface_DEFINED
+#define AndroidKit_Surface_DEFINED
+
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSurface.h"
+
+#include <jni.h>
+#include <android/native_window_jni.h>
+#include <android/native_window.h>
+
+#include "tools/sk_app/WindowContext.h"
+
+#include "include/core/SkPictureRecorder.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSurface.h"
+#include "include/core/SkTypes.h"
+
+class SurfaceThread;
+#include "modules/androidkit/src/SurfaceThread.h"
+
+class Surface : public SkRefCnt {
+public:
+    virtual void release(JNIEnv*) = 0;
+    virtual void flushAndSubmit() = 0;
+    virtual SkCanvas* getCanvas() = 0;
+
+    int width()  const { return fSurface ? fSurface->width()  : 0; }
+    int height() const { return fSurface ? fSurface->height() : 0; }
+
+protected:
+    sk_sp<SkSurface> fSurface;
+};
+
+class WindowSurface final : public Surface {
+public:
+    WindowSurface(ANativeWindow* win, std::unique_ptr<sk_app::WindowContext> wctx);
+
+private:
+    void release(JNIEnv* env) override;
+    SkCanvas* getCanvas() override;
+    void flushAndSubmit() override;
+
+    ANativeWindow*                         fWindow;
+    std::unique_ptr<sk_app::WindowContext> fWindowContext;
+};
+
+class ThreadedSurface final : public Surface {
+public:
+    ThreadedSurface(JNIEnv* env, jobject surface);
+
+private:
+    void release(JNIEnv* env) override;
+    SkCanvas* getCanvas() override;
+    void flushAndSubmit() override;
+
+    WindowSurface* fWindowSurface = nullptr;
+    SkPictureRecorder fRecorder;
+    std::unique_ptr<SurfaceThread> fThread;
+    int fWidth;
+    int fHeight;
+};
+
+#endif
diff --git a/modules/androidkit/src/SurfaceThread.cpp b/modules/androidkit/src/SurfaceThread.cpp
index 7ddc2c2..7c4a382 100644
--- a/modules/androidkit/src/SurfaceThread.cpp
+++ b/modules/androidkit/src/SurfaceThread.cpp
@@ -6,14 +6,15 @@
  */
 #include "modules/androidkit/src/SurfaceThread.h"
 
-#include <pthread.h>
-#include <android/looper.h>
+#include "tools/sk_app/WindowContext.h"
+#include "tools/sk_app/android/WindowContextFactory_android.h"
+
+#include "include/core/SkCanvas.h"
 #include "include/core/SkTypes.h"
 
-
 SurfaceThread::SurfaceThread() {
-    SkDebugf("initialized");
     pipe(fPipe);
+    fRunning = true;
     pthread_create(&fThread, nullptr, pthread_main, this);
 }
 
@@ -25,6 +26,10 @@
     read(fPipe[0], message, sizeof(Message));
 }
 
+void SurfaceThread::release() {
+    pthread_join(fThread, nullptr);
+}
+
 int SurfaceThread::message_callback(int /* fd */, int /* events */, void* data) {
     auto surfaceThread = (SurfaceThread*)data;
     Message message;
@@ -33,17 +38,30 @@
 
     switch (message.fType) {
         case kInitialize: {
-            SkDebugf("initialize WindowContext");
+            sk_app::DisplayParams params;
+            auto winctx = sk_app::window_context_factory::MakeGLForAndroid(message.fNativeWindow, params);
+            if (!winctx) {
+                break;
+            }
+            *message.fWindowSurface = new WindowSurface(message.fNativeWindow, std::move(winctx));
             break;
         }
         case kDestroy: {
-            SkDebugf("surface destroyed, shut down thread");
+            SkDebugf("surface destroyed, shutting down thread");
+            surfaceThread->fRunning = false;
+            if(auto* windowSurface = reinterpret_cast<Surface*>(*message.fWindowSurface)){
+                windowSurface->release(nullptr);
+                delete windowSurface;
+            }
             return 0;
             break;
         }
         case kRenderPicture: {
             sk_sp<SkPicture> picture(message.fPicture);
-            SkDebugf("take in picture and surface from message and call surface.getCanvas().drawPicture()");
+            if(auto* windowSurface = reinterpret_cast<Surface*>(*message.fWindowSurface)){
+                windowSurface->getCanvas()->drawPicture(picture);
+                windowSurface->flushAndSubmit();
+            }
             break;
         }
         default: {
@@ -61,7 +79,7 @@
     ALooper_addFd(looper, surfaceThread->fPipe[0], 1, ALOOPER_EVENT_INPUT,
                surfaceThread->message_callback, surfaceThread);
 
-    while (true) {
+    while (surfaceThread->fRunning) {
         const int ident = ALooper_pollAll(0, nullptr, nullptr, nullptr);
 
         if (ident >= 0) {
diff --git a/modules/androidkit/src/SurfaceThread.h b/modules/androidkit/src/SurfaceThread.h
index 992a30f..403d93f 100644
--- a/modules/androidkit/src/SurfaceThread.h
+++ b/modules/androidkit/src/SurfaceThread.h
@@ -4,13 +4,22 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#ifndef AndroidKit_SurfaceThread_DEFINED
+#define AndroidKit_SurfaceThread_DEFINED
+
 #include <pthread.h>
 #include <unistd.h>
 #include <android/looper.h>
 #include <android/native_window.h>
 
+#include "tools/sk_app/DisplayParams.h"
+
 #include "include/core/SkPictureRecorder.h"
 
+class WindowSurface;
+
+#include "modules/androidkit/src/Surface.h"
+
 enum MessageType {
     kUndefined,
     kInitialize,
@@ -21,7 +30,8 @@
 struct Message {
     MessageType fType = kUndefined;
     ANativeWindow* fNativeWindow = nullptr;
-    SkPicture* fPicture;
+    SkPicture* fPicture = nullptr;
+    WindowSurface** fWindowSurface = nullptr;
 
     Message() {}
     Message(MessageType t) : fType(t) {}
@@ -33,11 +43,16 @@
 
     void postMessage(const Message& message) const;
     void readMessage(Message* message) const;
-
+    void release();
 private:
     static void* pthread_main(void* arg);
     static int message_callback(int fd, int events, void* data);
+    // TODO: This has to be static, which is weird now, but fine in a singleton
+    // Switch to singleton design or find other way to break out of thread loop
+    bool fRunning;
 
     pthread_t fThread;
     int fPipe[2]; // acts as a Message queue, read from [0] write to [1]
 };
+
+#endif
diff --git a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
index b30db9c..94ab98e 100644
--- a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
+++ b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
@@ -17,6 +17,8 @@
 import org.skia.androidkit.*;
 
 public class MainActivity extends Activity implements SurfaceHolder.Callback {
+    public Surface surfaceSurface;
+
     static {
         System.loadLibrary("androidkit");
     }
@@ -51,19 +53,19 @@
         image.setImageBitmap(bmp);
 
         //Surface
-        Surface surfaceSurface;
         SurfaceView surfaceView = findViewById(R.id.surface);
         surfaceView.getHolder().addCallback(this);
     }
 
     @Override
     public void surfaceCreated(@NonNull SurfaceHolder holder) {
-        Surface surfaceSurface = Surface.createThreadedSurface(holder.getSurface());
     }
 
     @Override
     public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
-
+        surfaceSurface = Surface.createThreadedSurface(holder.getSurface());
+        surfaceSurface.getCanvas().drawColor(0xffffffe0);
+        surfaceSurface.flushAndSubmit();
     }
 
     @Override