diff options
author | 2017-11-21 13:18:02 -0500 | |
---|---|---|
committer | 2017-11-21 18:37:19 +0000 | |
commit | eff04b5ec287e0fee0d44207c10d2d11f7eade8a (patch) | |
tree | ea2cf00ea329c81611536aaa9a9f1eca47c67e9a /tools/sk_app/android | |
parent | 2aa09dbe8aced37aa6bb285e62df45deb0e81650 (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.cpp | 170 | ||||
-rw-r--r-- | tools/sk_app/android/RasterWindowContext_android.cpp | 108 | ||||
-rw-r--r-- | tools/sk_app/android/VulkanWindowContext_android.cpp | 58 | ||||
-rw-r--r-- | tools/sk_app/android/WindowContextFactory_android.h | 32 | ||||
-rw-r--r-- | tools/sk_app/android/Window_android.cpp | 85 | ||||
-rw-r--r-- | tools/sk_app/android/Window_android.h | 43 | ||||
-rw-r--r-- | tools/sk_app/android/main_android.cpp | 65 | ||||
-rw-r--r-- | tools/sk_app/android/surface_glue_android.cpp | 278 | ||||
-rw-r--r-- | tools/sk_app/android/surface_glue_android.h | 79 |
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 |