summaryrefslogtreecommitdiffstats
path: root/dom/plugins/base/android/ANPSurface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/base/android/ANPSurface.cpp')
-rw-r--r--dom/plugins/base/android/ANPSurface.cpp266
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;
+}