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