aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/sk_app/android
diff options
context:
space:
mode:
authorGravatar Brian Osman <brianosman@google.com>2017-11-21 13:18:02 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-11-21 18:37:19 +0000
commiteff04b5ec287e0fee0d44207c10d2d11f7eade8a (patch)
treeea2cf00ea329c81611536aaa9a9f1eca47c67e9a /tools/sk_app/android
parent2aa09dbe8aced37aa6bb285e62df45deb0e81650 (diff)
Remove SampleApp and convert HelloWorld to sk_app
There is still a large amount of views code that could be trimmed down, but which is used to implement samples (in viewer). Seemed simpler to remove some of this code in pieces. Bug: skia: Change-Id: Ia3415060d03c8de604a154e3dc38379b754daab6 Reviewed-on: https://skia-review.googlesource.com/72801 Commit-Queue: Brian Osman <brianosman@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
Diffstat (limited to 'tools/sk_app/android')
-rw-r--r--tools/sk_app/android/GLWindowContext_android.cpp170
-rw-r--r--tools/sk_app/android/RasterWindowContext_android.cpp108
-rw-r--r--tools/sk_app/android/VulkanWindowContext_android.cpp58
-rw-r--r--tools/sk_app/android/WindowContextFactory_android.h32
-rw-r--r--tools/sk_app/android/Window_android.cpp85
-rw-r--r--tools/sk_app/android/Window_android.h43
-rw-r--r--tools/sk_app/android/main_android.cpp65
-rw-r--r--tools/sk_app/android/surface_glue_android.cpp278
-rw-r--r--tools/sk_app/android/surface_glue_android.h79
9 files changed, 918 insertions, 0 deletions
diff --git a/tools/sk_app/android/GLWindowContext_android.cpp b/tools/sk_app/android/GLWindowContext_android.cpp
new file mode 100644
index 0000000000..944865909b
--- /dev/null
+++ b/tools/sk_app/android/GLWindowContext_android.cpp
@@ -0,0 +1,170 @@
+
+/*
+ * 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 <EGL/egl.h>
+#include <GLES/gl.h>
+#include "../GLWindowContext.h"
+#include "WindowContextFactory_android.h"
+#include "gl/GrGLInterface.h"
+
+using sk_app::GLWindowContext;
+using sk_app::DisplayParams;
+
+namespace {
+class GLWindowContext_android : public GLWindowContext {
+public:
+
+ GLWindowContext_android(ANativeWindow*, const DisplayParams&);
+
+ ~GLWindowContext_android() override;
+
+ void onSwapBuffers() override;
+
+ sk_sp<const GrGLInterface> onInitializeContext() override;
+ void onDestroyContext() override;
+
+private:
+
+ EGLDisplay fDisplay;
+ EGLContext fEGLContext;
+ EGLSurface fSurfaceAndroid;
+
+ // For setDisplayParams and resize which call onInitializeContext with null platformData
+ ANativeWindow* fNativeWindow = nullptr;
+
+ typedef GLWindowContext INHERITED;
+};
+
+GLWindowContext_android::GLWindowContext_android(ANativeWindow* window,
+ const DisplayParams& params)
+ : INHERITED(params)
+ , fDisplay(EGL_NO_DISPLAY)
+ , fEGLContext(EGL_NO_CONTEXT)
+ , fSurfaceAndroid(EGL_NO_SURFACE)
+ , fNativeWindow(window) {
+
+ // any config code here (particularly for msaa)?
+
+ this->initializeContext();
+}
+
+GLWindowContext_android::~GLWindowContext_android() {
+ this->destroyContext();
+}
+
+sk_sp<const GrGLInterface> GLWindowContext_android::onInitializeContext() {
+ fWidth = ANativeWindow_getWidth(fNativeWindow);
+ fHeight = ANativeWindow_getHeight(fNativeWindow);
+
+ fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ eglInitialize(fDisplay, &majorVersion, &minorVersion);
+
+ SkAssertResult(eglBindAPI(EGL_OPENGL_ES_API));
+
+ EGLint numConfigs = 0;
+ const EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_STENCIL_SIZE, 8,
+ EGL_SAMPLE_BUFFERS, fDisplayParams.fMSAASampleCount ? 1 : 0,
+ EGL_SAMPLES, fDisplayParams.fMSAASampleCount,
+ EGL_NONE
+ };
+
+ EGLConfig surfaceConfig;
+ SkAssertResult(eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs));
+ SkASSERT(numConfigs > 0);
+
+ static const EGLint kEGLContextAttribsForOpenGLES[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ fEGLContext = eglCreateContext(
+ fDisplay, surfaceConfig, nullptr, kEGLContextAttribsForOpenGLES);
+ SkASSERT(EGL_NO_CONTEXT != fEGLContext);
+
+// SkDebugf("EGL: %d.%d", majorVersion, minorVersion);
+// SkDebugf("Vendor: %s", eglQueryString(fDisplay, EGL_VENDOR));
+// SkDebugf("Extensions: %s", eglQueryString(fDisplay, EGL_EXTENSIONS));
+
+ // These values are the same as the corresponding VG colorspace attributes,
+ // which were accepted starting in EGL 1.2. For some reason in 1.4, sRGB
+ // became hidden behind an extension, but it looks like devices aren't
+ // advertising that extension (including Nexus 5X). So just check version?
+ const EGLint srgbWindowAttribs[] = {
+ /*EGL_GL_COLORSPACE_KHR*/ 0x309D, /*EGL_GL_COLORSPACE_SRGB_KHR*/ 0x3089,
+ EGL_NONE,
+ };
+ const EGLint* windowAttribs = nullptr;
+ auto srgbColorSpace = SkColorSpace::MakeSRGB();
+ if (srgbColorSpace == fDisplayParams.fColorSpace && majorVersion == 1 && minorVersion >= 2) {
+ windowAttribs = srgbWindowAttribs;
+ }
+
+ fSurfaceAndroid = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, windowAttribs);
+ if (EGL_NO_SURFACE == fSurfaceAndroid && windowAttribs) {
+ // Try again without sRGB
+ fSurfaceAndroid = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, nullptr);
+ }
+ SkASSERT(EGL_NO_SURFACE != fSurfaceAndroid);
+
+ SkAssertResult(eglMakeCurrent(fDisplay, fSurfaceAndroid, fSurfaceAndroid, fEGLContext));
+ // GLWindowContext::initializeContext will call GrGLCreateNativeInterface so we
+ // won't call it here.
+
+ glClearStencil(0);
+ glClearColor(0, 0, 0, 0);
+ glStencilMask(0xffffffff);
+ glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+ eglGetConfigAttrib(fDisplay, surfaceConfig, EGL_STENCIL_SIZE, &fStencilBits);
+ eglGetConfigAttrib(fDisplay, surfaceConfig, EGL_SAMPLES, &fSampleCount);
+
+ return sk_sp<const GrGLInterface>(GrGLCreateNativeInterface());
+}
+
+void GLWindowContext_android::onDestroyContext() {
+ if (!fDisplay || !fEGLContext || !fSurfaceAndroid) {
+ return;
+ }
+ eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ SkAssertResult(eglDestroySurface(fDisplay, fSurfaceAndroid));
+ SkAssertResult(eglDestroyContext(fDisplay, fEGLContext));
+ fEGLContext = EGL_NO_CONTEXT;
+ fSurfaceAndroid = EGL_NO_SURFACE;
+}
+
+void GLWindowContext_android::onSwapBuffers() {
+ if (fDisplay && fEGLContext && fSurfaceAndroid) {
+ eglSwapBuffers(fDisplay, fSurfaceAndroid);
+ }
+}
+
+} // anonymous namespace
+
+namespace sk_app {
+namespace window_context_factory {
+
+WindowContext* NewGLForAndroid(ANativeWindow* window, const DisplayParams& params) {
+ WindowContext* ctx = new GLWindowContext_android(window, params);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+} // namespace sk_app
diff --git a/tools/sk_app/android/RasterWindowContext_android.cpp b/tools/sk_app/android/RasterWindowContext_android.cpp
new file mode 100644
index 0000000000..101e51ef42
--- /dev/null
+++ b/tools/sk_app/android/RasterWindowContext_android.cpp
@@ -0,0 +1,108 @@
+
+/*
+ * 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 "WindowContextFactory_android.h"
+#include "../RasterWindowContext.h"
+#include "SkSurface.h"
+#include "SkTypes.h"
+
+using sk_app::RasterWindowContext;
+using sk_app::DisplayParams;
+
+namespace {
+class RasterWindowContext_android : public RasterWindowContext {
+public:
+ RasterWindowContext_android(ANativeWindow*, const DisplayParams& params);
+
+ sk_sp<SkSurface> getBackbufferSurface() override;
+ void swapBuffers() override;
+
+ bool isValid() override { return SkToBool(fNativeWindow); }
+ void resize(int w, int h) override;
+ void setDisplayParams(const DisplayParams& params) override;
+
+private:
+ void setBuffersGeometry();
+ sk_sp<SkSurface> fBackbufferSurface = nullptr;
+ ANativeWindow* fNativeWindow = nullptr;
+ ANativeWindow_Buffer fBuffer;
+ ARect fBounds;
+
+ typedef RasterWindowContext INHERITED;
+};
+
+RasterWindowContext_android::RasterWindowContext_android(ANativeWindow* window,
+ const DisplayParams& params)
+ : INHERITED(params) {
+ fNativeWindow = window;
+ fWidth = ANativeWindow_getWidth(fNativeWindow);
+ fHeight = ANativeWindow_getHeight(fNativeWindow);
+ this->setBuffersGeometry();
+}
+
+void RasterWindowContext_android::setBuffersGeometry() {
+ int32_t format = 0;
+ switch(fDisplayParams.fColorType) {
+ case kRGBA_8888_SkColorType:
+ format = WINDOW_FORMAT_RGBA_8888;
+ break;
+ case kRGB_565_SkColorType:
+ format = WINDOW_FORMAT_RGB_565;
+ break;
+ default:
+ SK_ABORT("Unsupported Android color type");
+ }
+ ANativeWindow_setBuffersGeometry(fNativeWindow, fWidth, fHeight, format);
+}
+
+void RasterWindowContext_android::setDisplayParams(const DisplayParams& params) {
+ fDisplayParams = params;
+ this->setBuffersGeometry();
+}
+
+void RasterWindowContext_android::resize(int w, int h) {
+ fWidth = w;
+ fHeight = h;
+ this->setBuffersGeometry();
+}
+
+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,
+ kPremul_SkAlphaType,
+ fDisplayParams.fColorSpace);
+ fBackbufferSurface = SkSurface::MakeRasterDirect(
+ info, fBuffer.bits, fBuffer.stride * bytePerPixel, nullptr);
+ }
+ return fBackbufferSurface;
+}
+
+
+void RasterWindowContext_android::swapBuffers() {
+ ANativeWindow_unlockAndPost(fNativeWindow);
+ fBackbufferSurface.reset(nullptr);
+}
+} // anonymous namespace
+
+namespace sk_app {
+namespace window_context_factory {
+
+WindowContext* NewRasterForAndroid(ANativeWindow* window, const DisplayParams& params) {
+ WindowContext* ctx = new RasterWindowContext_android(window, params);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+
+}
+} // namespace sk_app
diff --git a/tools/sk_app/android/VulkanWindowContext_android.cpp b/tools/sk_app/android/VulkanWindowContext_android.cpp
new file mode 100644
index 0000000000..a7d8aa7ea1
--- /dev/null
+++ b/tools/sk_app/android/VulkanWindowContext_android.cpp
@@ -0,0 +1,58 @@
+
+/*
+ * 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 "WindowContextFactory_android.h"
+#include "../VulkanWindowContext.h"
+
+#include "vk/VkTestUtils.h"
+
+namespace sk_app {
+
+namespace window_context_factory {
+
+WindowContext* NewVulkanForAndroid(ANativeWindow* window, const DisplayParams& params) {
+ PFN_vkGetInstanceProcAddr instProc;
+ PFN_vkGetDeviceProcAddr devProc;
+ if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) {
+ return nullptr;
+ }
+
+ auto createVkSurface = [window, instProc] (VkInstance instance) -> VkSurfaceKHR {
+ PFN_vkCreateAndroidSurfaceKHR createAndroidSurfaceKHR =
+ (PFN_vkCreateAndroidSurfaceKHR) instProc(instance, "vkCreateAndroidSurfaceKHR");
+
+ if (!window) {
+ return VK_NULL_HANDLE;
+ }
+ VkSurfaceKHR surface;
+
+ VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
+ memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.pNext = nullptr;
+ surfaceCreateInfo.flags = 0;
+ surfaceCreateInfo.window = window;
+
+ VkResult res = createAndroidSurfaceKHR(instance, &surfaceCreateInfo,
+ nullptr, &surface);
+ return (VK_SUCCESS == res) ? surface : VK_NULL_HANDLE;
+ };
+
+ auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; };
+
+ WindowContext* ctx = new VulkanWindowContext(params, createVkSurface, canPresent,
+ instProc, devProc);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+} // namespace sk_app
diff --git a/tools/sk_app/android/WindowContextFactory_android.h b/tools/sk_app/android/WindowContextFactory_android.h
new file mode 100644
index 0000000000..00198da8d3
--- /dev/null
+++ b/tools/sk_app/android/WindowContextFactory_android.h
@@ -0,0 +1,32 @@
+
+/*
+ * 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 WindowContextFactory_android_DEFINED
+#define WindowContextFactory_android_DEFINED
+
+#include <android/native_window_jni.h>
+
+
+namespace sk_app {
+
+class WindowContext;
+struct DisplayParams;
+
+namespace window_context_factory {
+
+WindowContext* NewVulkanForAndroid(ANativeWindow*, const DisplayParams&);
+
+WindowContext* NewGLForAndroid(ANativeWindow*, const DisplayParams&);
+
+WindowContext* NewRasterForAndroid(ANativeWindow*, const DisplayParams&);
+
+} // namespace window_context_factory
+
+} // namespace sk_app
+
+#endif
diff --git a/tools/sk_app/android/Window_android.cpp b/tools/sk_app/android/Window_android.cpp
new file mode 100644
index 0000000000..96acfc6564
--- /dev/null
+++ b/tools/sk_app/android/Window_android.cpp
@@ -0,0 +1,85 @@
+/*
+* 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 "Window_android.h"
+#include "WindowContextFactory_android.h"
+#include "../WindowContext.h"
+
+namespace sk_app {
+
+Window* Window::CreateNativeWindow(void* platformData) {
+ Window_android* window = new Window_android();
+ if (!window->init((SkiaAndroidApp*)platformData)) {
+ delete window;
+ return nullptr;
+ }
+ return window;
+}
+
+bool Window_android::init(SkiaAndroidApp* skiaAndroidApp) {
+ SkASSERT(skiaAndroidApp);
+ fSkiaAndroidApp = skiaAndroidApp;
+ fSkiaAndroidApp->fWindow = this;
+ return true;
+}
+
+void Window_android::setTitle(const char* title) {
+ fSkiaAndroidApp->setTitle(title);
+}
+
+void Window_android::setUIState(const char* state) {
+ fSkiaAndroidApp->setUIState(state);
+}
+
+bool Window_android::attach(BackendType attachType) {
+ fBackendType = attachType;
+
+ // 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;
+}
+
+void Window_android::initDisplay(ANativeWindow* window) {
+ SkASSERT(window);
+ switch (fBackendType) {
+ case kNativeGL_BackendType:
+ default:
+ fWindowContext = window_context_factory::NewGLForAndroid(window,
+ fRequestedDisplayParams);
+ break;
+ case kRaster_BackendType:
+ fWindowContext = window_context_factory::NewRasterForAndroid(window,
+ fRequestedDisplayParams);
+ break;
+#ifdef SK_VULKAN
+ case kVulkan_BackendType:
+ fWindowContext = window_context_factory::NewVulkanForAndroid(window,
+ fRequestedDisplayParams);
+ break;
+#endif
+ }
+ this->onBackendCreated();
+}
+
+void Window_android::onDisplayDestroyed() {
+ detach();
+}
+
+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/sk_app/android/Window_android.h b/tools/sk_app/android/Window_android.h
new file mode 100644
index 0000000000..9e28a8075b
--- /dev/null
+++ b/tools/sk_app/android/Window_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 Window_android_DEFINED
+#define Window_android_DEFINED
+
+#include "../Window.h"
+#include "surface_glue_android.h"
+
+namespace sk_app {
+
+class Window_android : public Window {
+public:
+ Window_android() : Window() {}
+ ~Window_android() override {}
+
+ bool init(SkiaAndroidApp* skiaAndroidApp);
+ void initDisplay(ANativeWindow* window);
+ void onDisplayDestroyed();
+
+ void setTitle(const char*) override;
+ void show() override {}
+
+ bool attach(BackendType) override;
+ void onInval() override;
+ void setUIState(const char* state) override;
+
+ void paintIfNeeded();
+
+ bool scaleContentToFit() const override { return true; }
+
+private:
+ SkiaAndroidApp* fSkiaAndroidApp = nullptr;
+ BackendType fBackendType;
+};
+
+} // namespace sk_app
+
+#endif
diff --git a/tools/sk_app/android/main_android.cpp b/tools/sk_app/android/main_android.cpp
new file mode 100644
index 0000000000..cb8db6c3b4
--- /dev/null
+++ b/tools/sk_app/android/main_android.cpp
@@ -0,0 +1,65 @@
+/*
+* 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 <jni.h>
+#include <errno.h>
+
+#include <android_native_app_glue.h>
+
+#include "../Application.h"
+#include "Timer.h"
+
+using sk_app::Application;
+
+/**
+ * This is the main entry point of a native application that is using
+ * android_native_app_glue. It runs in its own thread, with its own
+ * event loop for receiving input events and doing other things.
+ */
+void android_main(struct android_app* state) {
+ // Make sure glue isn't stripped.
+ app_dummy();
+
+ static const char* gCmdLine[] = {
+ "viewer",
+ "--skps",
+ "/data/local/tmp/skps",
+ // TODO: figure out how to use am start with extra params to pass in additional arguments at
+ // runtime
+ // "--atrace",
+ };
+
+ std::unique_ptr<Application> vkApp(Application::Create(SK_ARRAY_COUNT(gCmdLine),
+ const_cast<char**>(gCmdLine),
+ state));
+
+ // loop waiting for stuff to do.
+ while (1) {
+ // Read all pending events.
+ int ident;
+ int events;
+ struct android_poll_source* source;
+
+ // block forever waiting for events.
+ while ((ident=ALooper_pollAll(-1, NULL, &events,
+ (void**)&source)) >= 0) {
+
+ // Process this event.
+ if (source != NULL) {
+ source->process(state, source);
+ }
+
+ // Check if we are exiting.
+ if (state->destroyRequested != 0) {
+ return;
+ }
+
+ vkApp->onIdle();
+ }
+ }
+}
+//END_INCLUDE(all)
diff --git a/tools/sk_app/android/surface_glue_android.cpp b/tools/sk_app/android/surface_glue_android.cpp
new file mode 100644
index 0000000000..9c734247db
--- /dev/null
+++ b/tools/sk_app/android/surface_glue_android.cpp
@@ -0,0 +1,278 @@
+/*
+* 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 "surface_glue_android.h"
+
+#include <jni.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <unordered_map>
+
+#include <android/input.h>
+#include <android/keycodes.h>
+#include <android/looper.h>
+#include <android/native_window_jni.h>
+
+#include "../Application.h"
+#include "SkTypes.h"
+#include "SkUtils.h"
+#include "Window_android.h"
+
+namespace sk_app {
+
+static const int LOOPER_ID_MESSAGEPIPE = 1;
+
+static const std::unordered_map<int, Window::Key> ANDROID_TO_WINDOW_KEYMAP({
+ {AKEYCODE_SOFT_LEFT, Window::Key::kLeft},
+ {AKEYCODE_SOFT_RIGHT, Window::Key::kRight}
+});
+
+static const std::unordered_map<int, Window::InputState> ANDROID_TO_WINDOW_STATEMAP({
+ {AMOTION_EVENT_ACTION_DOWN, Window::kDown_InputState},
+ {AMOTION_EVENT_ACTION_POINTER_DOWN, Window::kDown_InputState},
+ {AMOTION_EVENT_ACTION_UP, Window::kUp_InputState},
+ {AMOTION_EVENT_ACTION_POINTER_UP, Window::kUp_InputState},
+ {AMOTION_EVENT_ACTION_MOVE, Window::kMove_InputState},
+ {AMOTION_EVENT_ACTION_CANCEL, Window::kUp_InputState},
+});
+
+SkiaAndroidApp::SkiaAndroidApp(JNIEnv* env, jobject androidApp) {
+ env->GetJavaVM(&fJavaVM);
+ fAndroidApp = env->NewGlobalRef(androidApp);
+ jclass cls = env->GetObjectClass(fAndroidApp);
+ fSetTitleMethodID = env->GetMethodID(cls, "setTitle", "(Ljava/lang/String;)V");
+ fSetStateMethodID = env->GetMethodID(cls, "setState", "(Ljava/lang/String;)V");
+ fNativeWindow = nullptr;
+ pthread_create(&fThread, nullptr, pthread_main, this);
+}
+
+SkiaAndroidApp::~SkiaAndroidApp() {
+ fPThreadEnv->DeleteGlobalRef(fAndroidApp);
+ if (fWindow) {
+ fWindow->detach();
+ }
+ if (fNativeWindow) {
+ ANativeWindow_release(fNativeWindow);
+ fNativeWindow = nullptr;
+ }
+ if (fApp) {
+ delete fApp;
+ }
+}
+
+void SkiaAndroidApp::setTitle(const char* title) const {
+ jstring titleString = fPThreadEnv->NewStringUTF(title);
+ fPThreadEnv->CallVoidMethod(fAndroidApp, fSetTitleMethodID, titleString);
+ fPThreadEnv->DeleteLocalRef(titleString);
+}
+
+void SkiaAndroidApp::setUIState(const char* state) const {
+ jstring jstr = fPThreadEnv->NewStringUTF(state);
+ fPThreadEnv->CallVoidMethod(fAndroidApp, fSetStateMethodID, jstr);
+ fPThreadEnv->DeleteLocalRef(jstr);
+}
+
+void SkiaAndroidApp::postMessage(const Message& message) const {
+ SkDEBUGCODE(auto writeSize =) write(fPipes[1], &message, sizeof(message));
+ SkASSERT(writeSize == sizeof(message));
+}
+
+void SkiaAndroidApp::readMessage(Message* message) const {
+ SkDEBUGCODE(auto readSize =) read(fPipes[0], message, sizeof(Message));
+ SkASSERT(readSize == sizeof(Message));
+}
+
+int SkiaAndroidApp::message_callback(int fd, int events, void* data) {
+ auto skiaAndroidApp = (SkiaAndroidApp*)data;
+ Message message;
+ skiaAndroidApp->readMessage(&message);
+ SkASSERT(message.fType != kUndefined);
+
+ switch (message.fType) {
+ case kDestroyApp: {
+ delete skiaAndroidApp;
+ pthread_exit(nullptr);
+ return 0;
+ }
+ case kContentInvalidated: {
+ ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
+ break;
+ }
+ case kSurfaceCreated: {
+ SkASSERT(!skiaAndroidApp->fNativeWindow && message.fNativeWindow);
+ skiaAndroidApp->fNativeWindow = message.fNativeWindow;
+ auto window_android = (Window_android*)skiaAndroidApp->fWindow;
+ window_android->initDisplay(skiaAndroidApp->fNativeWindow);
+ ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
+ break;
+ }
+ case kSurfaceChanged: {
+ SkASSERT(message.fNativeWindow);
+ int width = ANativeWindow_getWidth(skiaAndroidApp->fNativeWindow);
+ int height = ANativeWindow_getHeight(skiaAndroidApp->fNativeWindow);
+ auto window_android = (Window_android*)skiaAndroidApp->fWindow;
+ if (message.fNativeWindow != skiaAndroidApp->fNativeWindow) {
+ window_android->onDisplayDestroyed();
+ ANativeWindow_release(skiaAndroidApp->fNativeWindow);
+ skiaAndroidApp->fNativeWindow = message.fNativeWindow;
+ window_android->initDisplay(skiaAndroidApp->fNativeWindow);
+ }
+ window_android->onResize(width, height);
+ window_android->paintIfNeeded();
+ break;
+ }
+ case kSurfaceDestroyed: {
+ if (skiaAndroidApp->fNativeWindow) {
+ auto window_android = (Window_android*)skiaAndroidApp->fWindow;
+ window_android->onDisplayDestroyed();
+ ANativeWindow_release(skiaAndroidApp->fNativeWindow);
+ skiaAndroidApp->fNativeWindow = nullptr;
+ }
+ break;
+ }
+ case kKeyPressed: {
+ auto it = ANDROID_TO_WINDOW_KEYMAP.find(message.fKeycode);
+ SkASSERT(it != ANDROID_TO_WINDOW_KEYMAP.end());
+ // No modifier is supported so far
+ skiaAndroidApp->fWindow->onKey(it->second, Window::kDown_InputState, 0);
+ skiaAndroidApp->fWindow->onKey(it->second, Window::kUp_InputState, 0);
+ break;
+ }
+ case kTouched: {
+ auto it = ANDROID_TO_WINDOW_STATEMAP.find(message.fTouchState);
+ if (it != ANDROID_TO_WINDOW_STATEMAP.end()) {
+ skiaAndroidApp->fWindow->onTouch(message.fTouchOwner, it->second, message.fTouchX,
+ message.fTouchY);
+ } else {
+ SkDebugf("Unknown Touch State: %d\n", message.fTouchState);
+ }
+ break;
+ }
+ case kUIStateChanged: {
+ skiaAndroidApp->fWindow->onUIStateChanged(*message.stateName, *message.stateValue);
+ delete message.stateName;
+ delete message.stateValue;
+ break;
+ }
+ default: {
+ // do nothing
+ }
+ }
+
+ return 1; // continue receiving callbacks
+}
+
+void* SkiaAndroidApp::pthread_main(void* arg) {
+ SkDebugf("pthread_main begins");
+
+ auto skiaAndroidApp = (SkiaAndroidApp*)arg;
+
+ // Because JNIEnv is thread sensitive, we need AttachCurrentThread to set our fPThreadEnv
+ skiaAndroidApp->fJavaVM->AttachCurrentThread(&(skiaAndroidApp->fPThreadEnv), nullptr);
+
+ ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+ pipe(skiaAndroidApp->fPipes);
+ ALooper_addFd(looper, skiaAndroidApp->fPipes[0], LOOPER_ID_MESSAGEPIPE, ALOOPER_EVENT_INPUT,
+ message_callback, skiaAndroidApp);
+
+ static const char* gCmdLine[] = {
+ "viewer",
+ // TODO: figure out how to use am start with extra params to pass in additional arguments at
+ // runtime. Or better yet make an in app switch to enable
+ // "--atrace",
+ };
+
+ skiaAndroidApp->fApp = Application::Create(SK_ARRAY_COUNT(gCmdLine),
+ const_cast<char**>(gCmdLine),
+ skiaAndroidApp);
+
+ while (true) {
+ const int ident = ALooper_pollAll(0, nullptr, nullptr, nullptr);
+
+ if (ident >= 0) {
+ SkDebugf("Unhandled ALooper_pollAll ident=%d !", ident);
+ } else {
+ skiaAndroidApp->fApp->onIdle();
+ }
+ }
+
+ SkDebugf("pthread_main ends");
+
+ return nullptr;
+}
+
+extern "C" // extern "C" is needed for JNI (although the method itself is in C++)
+ JNIEXPORT jlong JNICALL
+ Java_org_skia_viewer_ViewerApplication_createNativeApp(JNIEnv* env, jobject application) {
+ SkiaAndroidApp* skiaAndroidApp = new SkiaAndroidApp(env, application);
+ return (jlong)((size_t)skiaAndroidApp);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerApplication_destroyNativeApp(
+ JNIEnv* env, jobject application, jlong handle) {
+ auto skiaAndroidApp = (SkiaAndroidApp*)handle;
+ skiaAndroidApp->postMessage(Message(kDestroyApp));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceCreated(
+ JNIEnv* env, jobject activity, jlong handle, jobject surface) {
+ auto skiaAndroidApp = (SkiaAndroidApp*)handle;
+ Message message(kSurfaceCreated);
+ message.fNativeWindow = ANativeWindow_fromSurface(env, surface);
+ skiaAndroidApp->postMessage(message);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceChanged(
+ JNIEnv* env, jobject activity, jlong handle, jobject surface) {
+ auto skiaAndroidApp = (SkiaAndroidApp*)handle;
+ Message message(kSurfaceChanged);
+ message.fNativeWindow = ANativeWindow_fromSurface(env, surface);
+ skiaAndroidApp->postMessage(message);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceDestroyed(
+ JNIEnv* env, jobject activity, jlong handle) {
+ auto skiaAndroidApp = (SkiaAndroidApp*)handle;
+ skiaAndroidApp->postMessage(Message(kSurfaceDestroyed));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onKeyPressed(JNIEnv* env,
+ jobject activity,
+ jlong handle,
+ jint keycode) {
+ auto skiaAndroidApp = (SkiaAndroidApp*)handle;
+ Message message(kKeyPressed);
+ message.fKeycode = keycode;
+ skiaAndroidApp->postMessage(message);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onTouched(
+ JNIEnv* env, jobject activity, jlong handle, jint owner, jint state, jfloat x, jfloat y) {
+ auto skiaAndroidApp = (SkiaAndroidApp*)handle;
+ Message message(kTouched);
+ message.fTouchOwner = owner;
+ message.fTouchState = state;
+ message.fTouchX = x;
+ message.fTouchY = y;
+ skiaAndroidApp->postMessage(message);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onUIStateChanged(
+ JNIEnv* env, jobject activity, jlong handle, jstring stateName, jstring stateValue) {
+ auto skiaAndroidApp = (SkiaAndroidApp*)handle;
+ Message message(kUIStateChanged);
+ const char* nameChars = env->GetStringUTFChars(stateName, nullptr);
+ const char* valueChars = env->GetStringUTFChars(stateValue, nullptr);
+ message.stateName = new SkString(nameChars);
+ message.stateValue = new SkString(valueChars);
+ skiaAndroidApp->postMessage(message);
+ env->ReleaseStringUTFChars(stateName, nameChars);
+ env->ReleaseStringUTFChars(stateValue, valueChars);
+}
+
+} // namespace sk_app
diff --git a/tools/sk_app/android/surface_glue_android.h b/tools/sk_app/android/surface_glue_android.h
new file mode 100644
index 0000000000..1dd1f2854a
--- /dev/null
+++ b/tools/sk_app/android/surface_glue_android.h
@@ -0,0 +1,79 @@
+/*
+* 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 surface_glue_android_DEFINED
+#define surface_glue_android_DEFINED
+
+#include <pthread.h>
+
+#include <android/native_window_jni.h>
+
+#include "SkString.h"
+
+#include "../Application.h"
+#include "../Window.h"
+
+namespace sk_app {
+
+enum MessageType {
+ kUndefined,
+ kSurfaceCreated,
+ kSurfaceChanged,
+ kSurfaceDestroyed,
+ kDestroyApp,
+ kContentInvalidated,
+ kKeyPressed,
+ kTouched,
+ kUIStateChanged,
+};
+
+struct Message {
+ MessageType fType = kUndefined;
+ ANativeWindow* fNativeWindow = nullptr;
+ int fKeycode = 0;
+ int fTouchOwner, fTouchState;
+ float fTouchX, fTouchY;
+
+ SkString* stateName;
+ SkString* stateValue;
+
+ Message() {}
+ Message(MessageType t) : fType(t) {}
+};
+
+struct SkiaAndroidApp {
+ Application* fApp;
+ Window* fWindow;
+ jobject fAndroidApp;
+
+ SkiaAndroidApp(JNIEnv* env, jobject androidApp);
+
+ void postMessage(const Message& message) const;
+ void readMessage(Message* message) const;
+
+ // These must be called in SkiaAndroidApp's own pthread because the JNIEnv is thread sensitive
+ void setTitle(const char* title) const;
+ void setUIState(const char* state) const;
+
+private:
+ pthread_t fThread;
+ ANativeWindow* fNativeWindow;
+ int fPipes[2]; // 0 is the read message pipe, 1 is the write message pipe
+ JavaVM* fJavaVM;
+ JNIEnv* fPThreadEnv;
+ jmethodID fSetTitleMethodID, fSetStateMethodID;
+
+ // This must be called in SkiaAndroidApp's own pthread because the JNIEnv is thread sensitive
+ ~SkiaAndroidApp();
+
+ static int message_callback(int fd, int events, void* data);
+ static void* pthread_main(void*);
+};
+
+} // namespace sk_app
+
+#endif