Implement Raster Backend on Android Viewer App

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

Review-Url: https://codereview.chromium.org/2041193004
diff --git a/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/StateAdapter.java b/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/StateAdapter.java
index d546c7b..5ee68ef 100644
--- a/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/StateAdapter.java
+++ b/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/StateAdapter.java
@@ -1,6 +1,7 @@
 package org.skia.viewer;
 
 import android.view.LayoutInflater;
+import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
@@ -30,6 +31,7 @@
     static final String NAME = "name";
     static final String VALUE = "value";
     static final String OPTIONS = "options";
+    private static final String BACKEND_STATE_NAME = "Backend";
 
     ViewerActivity mViewerActivity;
     LinearLayout mLayout;
@@ -146,6 +148,21 @@
             stateItem.setTag(null); // Reset the tag to let updateDrawer update this item view.
             mViewerActivity.onStateChanged(stateName, stateValue);
         }
+
+        // Due to the current Android limitation, we're required to recreate the SurfaceView for
+        // switching to/from the Raster backend.
+        // (Although we can switch between GPU backend without recreating the SurfaceView.)
+        final Object oldValue = stateItem.getTag(R.integer.value_tag_key);
+        if (stateName.equals(BACKEND_STATE_NAME)
+                && oldValue != null && !stateValue.equals(oldValue)) {
+            LinearLayout mainLayout = (LinearLayout) mViewerActivity.findViewById(R.id.mainLayout);
+            mainLayout.removeAllViews();
+            SurfaceView surfaceView = new SurfaceView(mViewerActivity);
+            surfaceView.setId(R.id.surfaceView);
+            surfaceView.getHolder().addCallback(mViewerActivity);
+            surfaceView.setOnTouchListener(mViewerActivity);
+            mainLayout.addView(surfaceView);
+        }
     }
 
     @Override
diff --git a/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerActivity.java b/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerActivity.java
index ce5bb0d..0291f5e 100644
--- a/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerActivity.java
+++ b/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerActivity.java
@@ -32,7 +32,6 @@
     private ListView mDrawerList;
     private StateAdapter mStateAdapter;
 
-    private SurfaceView mView;
     private ViewerApplication mApplication;
 
     private native void onSurfaceCreated(long handle, Surface surface);
@@ -74,10 +73,9 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
-        mView = (SurfaceView) findViewById(R.id.surfaceView);
-        mView.getHolder().addCallback(this);
-
-        mView.setOnTouchListener(this);
+        SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
+        surfaceView.getHolder().addCallback(this);
+        surfaceView.setOnTouchListener(this);
 
         mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
         mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index de4c4cc..7015dad 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -57,7 +57,8 @@
 
 const char *kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
     " [OpenGL]",
-    " [Vulkan]"
+    " [Vulkan]",
+    " [Raster]"
 };
 
 const char* kName = "name";
diff --git a/tools/viewer/sk_app/RasterWindowContext.h b/tools/viewer/sk_app/RasterWindowContext.h
new file mode 100644
index 0000000..f116b8e
--- /dev/null
+++ b/tools/viewer/sk_app/RasterWindowContext.h
@@ -0,0 +1,29 @@
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef RasterWindowContext_DEFINED
+#define RasterWindowContext_DEFINED
+
+#include "WindowContext.h"
+
+namespace sk_app {
+
+class RasterWindowContext : public WindowContext {
+public:
+    // This is defined in the platform .cpp file
+    static RasterWindowContext* Create(void* platformData, const DisplayParams& params);
+
+    // Explicitly convert nullptr to GrBackendContext is needed for compiling
+    GrBackendContext getBackendContext() override { return (GrBackendContext) nullptr; }
+
+protected:
+    bool isGpuContext() override { return false; }
+};
+
+}   // namespace sk_app
+
+#endif
diff --git a/tools/viewer/sk_app/Window.h b/tools/viewer/sk_app/Window.h
index 29c85b9..8b1378a 100644
--- a/tools/viewer/sk_app/Window.h
+++ b/tools/viewer/sk_app/Window.h
@@ -42,8 +42,9 @@
     enum BackendType {
         kNativeGL_BackendType,
         kVulkan_BackendType,
+        kRaster_BackendType,
 
-        kLast_BackendType = kVulkan_BackendType
+        kLast_BackendType = kRaster_BackendType
     };
     enum {
         kBackendTypeCount = kLast_BackendType + 1
diff --git a/tools/viewer/sk_app/android/RasterWindowContext_android.cpp b/tools/viewer/sk_app/android/RasterWindowContext_android.cpp
new file mode 100644
index 0000000..306f291
--- /dev/null
+++ b/tools/viewer/sk_app/android/RasterWindowContext_android.cpp
@@ -0,0 +1,69 @@
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "RasterWindowContext_android.h"
+
+#include "SkSurface.h"
+#include "SkTypes.h"
+
+#include "Window_android.h"
+
+namespace sk_app {
+
+RasterWindowContext* RasterWindowContext::Create(void* platformData, const DisplayParams& params) {
+    RasterWindowContext* ctx = new RasterWindowContext_android(platformData, params);
+    if (!ctx->isValid()) {
+        delete ctx;
+        ctx = nullptr;
+    }
+    return ctx;
+}
+
+RasterWindowContext_android::RasterWindowContext_android(
+        void* platformData, const DisplayParams& params) {
+    fDisplayParams = params;
+    ContextPlatformData_android* androidPlatformData =
+            reinterpret_cast<ContextPlatformData_android*>(platformData);
+    fNativeWindow = androidPlatformData->fNativeWindow;
+    fWidth = ANativeWindow_getWidth(fNativeWindow);
+    fHeight = ANativeWindow_getHeight(fNativeWindow);
+    int32_t format;
+    switch(params.fColorType) {
+        case kRGBA_8888_SkColorType:
+            format = WINDOW_FORMAT_RGBA_8888;
+            break;
+        case kRGB_565_SkColorType:
+            format = WINDOW_FORMAT_RGB_565;
+            break;
+        default:
+            SkDEBUGFAIL("Unsupported Android color type");
+    }
+    ANativeWindow_setBuffersGeometry(fNativeWindow, fWidth, fHeight, format);
+}
+
+sk_sp<SkSurface> RasterWindowContext_android::getBackbufferSurface() {
+    if (nullptr == fBackbufferSurface) {
+        ANativeWindow_lock(fNativeWindow, &fBuffer, &fBounds);
+        const int bytePerPixel = fBuffer.format == WINDOW_FORMAT_RGB_565 ? 2 : 4;
+        SkImageInfo info = SkImageInfo::Make(fWidth, fHeight,
+                                             fDisplayParams.fColorType,
+                                             kOpaque_SkAlphaType,
+                                             fDisplayParams.fProfileType);
+        fBackbufferSurface = SkSurface::MakeRasterDirect(
+                info, fBuffer.bits, fBuffer.stride * bytePerPixel, nullptr);
+    }
+    return fBackbufferSurface;
+}
+
+
+void RasterWindowContext_android::swapBuffers() {
+    ANativeWindow_unlockAndPost(fNativeWindow);
+    fBackbufferSurface.reset(nullptr);
+}
+
+}   // namespace sk_app
diff --git a/tools/viewer/sk_app/android/RasterWindowContext_android.h b/tools/viewer/sk_app/android/RasterWindowContext_android.h
new file mode 100644
index 0000000..913b06c
--- /dev/null
+++ b/tools/viewer/sk_app/android/RasterWindowContext_android.h
@@ -0,0 +1,43 @@
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef RasterWindowContext_android_DEFINED
+#define RasterWindowContext_android_DEFINED
+
+#include <android/native_window_jni.h>
+
+#include "../RasterWindowContext.h"
+
+namespace sk_app {
+
+class RasterWindowContext_android : public RasterWindowContext {
+public:
+    friend RasterWindowContext* RasterWindowContext::Create(
+            void* platformData, const DisplayParams&);
+
+    sk_sp<SkSurface> getBackbufferSurface() override;
+    void swapBuffers() override;
+
+    bool isValid() override { return SkToBool(fNativeWindow); }
+    void resize(uint32_t w, uint32_t h) override {
+        SkDEBUGFAIL("Resize is currently unsupported.");
+    }
+    void setDisplayParams(const DisplayParams& params) override {
+        SkDEBUGFAIL("setDisplayParams is currently unsupported.");
+    }
+
+private:
+    RasterWindowContext_android(void* platformData, const DisplayParams& params);
+    sk_sp<SkSurface> fBackbufferSurface = nullptr;
+    ANativeWindow* fNativeWindow = nullptr;
+    ANativeWindow_Buffer fBuffer;
+    ARect fBounds;
+};
+
+}   // namespace sk_app
+
+#endif
diff --git a/tools/viewer/sk_app/android/Window_android.cpp b/tools/viewer/sk_app/android/Window_android.cpp
index 926c7cd..94ba9b0 100644
--- a/tools/viewer/sk_app/android/Window_android.cpp
+++ b/tools/viewer/sk_app/android/Window_android.cpp
@@ -8,6 +8,7 @@
 #include "Window_android.h"
 #include "../GLWindowContext.h"
 #include "../VulkanWindowContext.h"
+#include "../RasterWindowContext.h"
 
 namespace sk_app {
 
@@ -49,11 +50,7 @@
     fBackendType = attachType;
     fDisplayParams = params;
 
-    if (fNativeWindow) {
-        this->initDisplay(fNativeWindow);
-    }
-    // If fNativeWindow is not set,
-    // we delay the creation of fWindowContext until Android informs us that
+    // We delay the creation of fWindowContext until Android informs us that
     // the native window is ready to use.
     // The creation will be done in initDisplay, which is initiated by kSurfaceCreated event.
     return true;
@@ -61,14 +58,15 @@
 
 void Window_android::initDisplay(ANativeWindow* window) {
     SkASSERT(window);
-    fNativeWindow = window;
     ContextPlatformData_android platformData;
     platformData.fNativeWindow = window;
     switch (fBackendType) {
         case kNativeGL_BackendType:
             fWindowContext = GLWindowContext::Create((void*)&platformData, fDisplayParams);
             break;
-
+        case kRaster_BackendType:
+            fWindowContext = RasterWindowContext::Create((void*)&platformData, fDisplayParams);
+            break;
         case kVulkan_BackendType:
         default:
             fWindowContext = VulkanWindowContext::Create((void*)&platformData, fDisplayParams);
@@ -78,7 +76,6 @@
 
 void Window_android::onDisplayDestroyed() {
     detach();
-    fNativeWindow = nullptr;
 }
 
 void Window_android::onInval() {
diff --git a/tools/viewer/sk_app/android/Window_android.h b/tools/viewer/sk_app/android/Window_android.h
index 2a7ff47..10c35d0 100644
--- a/tools/viewer/sk_app/android/Window_android.h
+++ b/tools/viewer/sk_app/android/Window_android.h
@@ -44,9 +44,6 @@
     void setContentRect(int l, int t, int r, int b) { fContentRect.set(l,t,r,b); }
 
 private:
-    // We need fNativeWindow for attaching with another backend.
-    // (in that case, attach is called without initDisplay being called later)
-    ANativeWindow* fNativeWindow = nullptr;
     SkiaAndroidApp* fSkiaAndroidApp = nullptr;
     SkRect fContentRect;
     DisplayParams fDisplayParams;