tenfourfox/dom/plugins/base/android/ANPSurface.cpp
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

267 lines
7.4 KiB
C++

/* -*- 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;
}