mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 14:15:30 +00:00
214bd6ec96
--HG-- extra : rebase_source : 827cf2bb75601a723a64593fca5c0cf6632c959c
267 lines
7.4 KiB
C++
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;
|
|
}
|