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