diff options
Diffstat (limited to 'dom/plugins/base/android/ANPSurface.cpp')
-rw-r--r-- | dom/plugins/base/android/ANPSurface.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/dom/plugins/base/android/ANPSurface.cpp b/dom/plugins/base/android/ANPSurface.cpp new file mode 100644 index 000000000..b6a699f28 --- /dev/null +++ b/dom/plugins/base/android/ANPSurface.cpp @@ -0,0 +1,266 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <dlfcn.h> +#include <android/log.h> +#include "ANPBase.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_surface_##name + +#define CLEAR_EXCEPTION(env) if (env->ExceptionOccurred()) env->ExceptionClear(); + +#define ANDROID_REGION_SIZE 512 + +enum { + PIXEL_FORMAT_RGBA_8888 = 1, + PIXEL_FORMAT_RGB_565 = 4, +}; + +struct SurfaceInfo { + uint32_t w; + uint32_t h; + uint32_t s; + uint32_t usage; + uint32_t format; + unsigned char* bits; + uint32_t reserved[2]; +}; + +typedef struct ARect { + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +} ARect; + + +// used to cache JNI method and field IDs for Surface Objects +static struct ANPSurfaceInterfaceJavaGlue { + bool initialized; + jmethodID getSurfaceHolder; + jmethodID getSurface; + jfieldID surfacePointer; +} gSurfaceJavaGlue; + +static struct ANPSurfaceFunctions { + bool initialized; + + int (* lock)(void*, SurfaceInfo*, void*); + int (* unlockAndPost)(void*); + + void* (* regionConstructor)(void*); + void (* setRegion)(void*, ARect const&); +} gSurfaceFunctions; + + +static inline void* getSurface(JNIEnv* env, jobject view) { + if (!env || !view) { + return nullptr; + } + + if (!gSurfaceJavaGlue.initialized) { + + jclass surfaceViewClass = env->FindClass("android/view/SurfaceView"); + gSurfaceJavaGlue.getSurfaceHolder = env->GetMethodID(surfaceViewClass, "getHolder", "()Landroid/view/SurfaceHolder;"); + + jclass surfaceHolderClass = env->FindClass("android/view/SurfaceHolder"); + gSurfaceJavaGlue.getSurface = env->GetMethodID(surfaceHolderClass, "getSurface", "()Landroid/view/Surface;"); + + jclass surfaceClass = env->FindClass("android/view/Surface"); + gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass, + "mSurfacePointer", "I"); + + if (!gSurfaceJavaGlue.surfacePointer) { + CLEAR_EXCEPTION(env); + + // It was something else in 2.2. + gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass, + "mSurface", "I"); + + if (!gSurfaceJavaGlue.surfacePointer) { + CLEAR_EXCEPTION(env); + + // And something else in 2.3+ + gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass, + "mNativeSurface", "I"); + + CLEAR_EXCEPTION(env); + } + } + + if (!gSurfaceJavaGlue.surfacePointer) { + LOG("Failed to acquire surface pointer"); + return nullptr; + } + + env->DeleteLocalRef(surfaceClass); + env->DeleteLocalRef(surfaceViewClass); + env->DeleteLocalRef(surfaceHolderClass); + + gSurfaceJavaGlue.initialized = (gSurfaceJavaGlue.surfacePointer != nullptr); + } + + jobject holder = env->CallObjectMethod(view, gSurfaceJavaGlue.getSurfaceHolder); + jobject surface = env->CallObjectMethod(holder, gSurfaceJavaGlue.getSurface); + jint surfacePointer = env->GetIntField(surface, gSurfaceJavaGlue.surfacePointer); + + env->DeleteLocalRef(holder); + env->DeleteLocalRef(surface); + + return (void*)surfacePointer; +} + +static ANPBitmapFormat convertPixelFormat(int32_t format) { + switch (format) { + case PIXEL_FORMAT_RGBA_8888: return kRGBA_8888_ANPBitmapFormat; + case PIXEL_FORMAT_RGB_565: return kRGB_565_ANPBitmapFormat; + default: return kUnknown_ANPBitmapFormat; + } +} + +static int bytesPerPixel(int32_t format) { + switch (format) { + case PIXEL_FORMAT_RGBA_8888: return 4; + case PIXEL_FORMAT_RGB_565: return 2; + default: return -1; + } +} + +static bool init() { + if (gSurfaceFunctions.initialized) + return true; + + void* handle = dlopen("libsurfaceflinger_client.so", RTLD_LAZY); + + if (!handle) { + LOG("Failed to open libsurfaceflinger_client.so"); + return false; + } + + gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionEb"); + gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv"); + + + if (!gSurfaceFunctions.lock) { + // Stuff changed in 3.0/4.0 + handle = dlopen("libgui.so", RTLD_LAZY); + gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE"); + gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv"); + } + + handle = dlopen("libui.so", RTLD_LAZY); + if (!handle) { + LOG("Failed to open libui.so"); + return false; + } + + gSurfaceFunctions.regionConstructor = (void* (*)(void*))dlsym(handle, "_ZN7android6RegionC1Ev"); + gSurfaceFunctions.setRegion = (void (*)(void*, ARect const&))dlsym(handle, "_ZN7android6Region3setERKNS_4RectE"); + + gSurfaceFunctions.initialized = (gSurfaceFunctions.lock && gSurfaceFunctions.unlockAndPost && + gSurfaceFunctions.regionConstructor && gSurfaceFunctions.setRegion); + LOG("Initialized? %d\n", gSurfaceFunctions.initialized); + return gSurfaceFunctions.initialized; +} + +// FIXME: All of this should be changed to use the equivalent things in AndroidBridge, bug 758612 +static bool anp_surface_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) { + if (!bitmap || !surfaceView) { + return false; + } + + void* surface = getSurface(env, surfaceView); + + if (!bitmap || !surface) { + return false; + } + + if (!init()) { + return false; + } + + void* region = nullptr; + if (dirtyRect) { + region = malloc(ANDROID_REGION_SIZE); + gSurfaceFunctions.regionConstructor(region); + + ARect rect; + rect.left = dirtyRect->left; + rect.top = dirtyRect->top; + rect.right = dirtyRect->right; + rect.bottom = dirtyRect->bottom; + + gSurfaceFunctions.setRegion(region, rect); + } + + SurfaceInfo info; + int err = gSurfaceFunctions.lock(surface, &info, region); + if (err < 0) { + LOG("Failed to lock surface"); + return false; + } + + // the surface may have expanded the dirty region so we must to pass that + // information back to the plugin. + if (dirtyRect) { + ARect* dirtyBounds = (ARect*)region; // The bounds are the first member, so this should work! + + dirtyRect->left = dirtyBounds->left; + dirtyRect->right = dirtyBounds->right; + dirtyRect->top = dirtyBounds->top; + dirtyRect->bottom = dirtyBounds->bottom; + } + + if (region) + free(region); + + int bpr = info.s * bytesPerPixel(info.format); + + bitmap->format = convertPixelFormat(info.format); + bitmap->width = info.w; + bitmap->height = info.h; + bitmap->rowBytes = bpr; + + if (info.w > 0 && info.h > 0) { + bitmap->baseAddr = info.bits; + } else { + bitmap->baseAddr = nullptr; + return false; + } + + return true; +} + +static void anp_surface_unlock(JNIEnv* env, jobject surfaceView) { + if (!surfaceView) { + return; + } + + if (!init()) { + return; + } + + void* surface = getSurface(env, surfaceView); + + if (!surface) { + return; + } + + gSurfaceFunctions.unlockAndPost(surface); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitSurfaceInterface(ANPSurfaceInterfaceV0* i) { + ASSIGN(i, lock); + ASSIGN(i, unlock); + + // setup the java glue struct + gSurfaceJavaGlue.initialized = false; + + // setup the function struct + gSurfaceFunctions.initialized = false; +} |