Bug 686992 - Draw to Android window/surface directly r=blassey

This commit is contained in:
James Willcox 2011-09-21 12:46:00 -04:00
parent f44def4369
commit baeb34d321
6 changed files with 263 additions and 85 deletions

View File

@ -248,75 +248,65 @@ class GeckoSurfaceView
mSurfaceLock.lock();
if (mInDrawing) {
Log.w(LOG_FILE_NAME, "surfaceChanged while mInDrawing is true!");
}
boolean invalidSize;
if (width == 0 || height == 0) {
mSoftwareBitmap = null;
mSoftwareBuffer = null;
mSoftwareBufferCopy = null;
invalidSize = true;
} else {
invalidSize = false;
}
boolean doSyncDraw =
mDrawMode == DRAW_2D &&
!invalidSize &&
GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning);
mSyncDraw = doSyncDraw;
mFormat = format;
mWidth = width;
mHeight = height;
mSurfaceValid = true;
Log.i(LOG_FILE_NAME, "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
try {
if (mInDrawing) {
Log.w(LOG_FILE_NAME, "surfaceChanged while mInDrawing is true!");
}
boolean invalidSize;
if (width == 0 || height == 0) {
mSoftwareBitmap = null;
mSoftwareBuffer = null;
mSoftwareBufferCopy = null;
invalidSize = true;
} else {
invalidSize = false;
}
boolean doSyncDraw =
mDrawMode == DRAW_2D &&
!invalidSize &&
GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning);
mSyncDraw = doSyncDraw;
mFormat = format;
mWidth = width;
mHeight = height;
mSurfaceValid = true;
Log.i(LOG_FILE_NAME, "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
DisplayMetrics metrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
GeckoEvent e = new GeckoEvent(GeckoEvent.SIZE_CHANGED, width, height,
metrics.widthPixels, metrics.heightPixels);
GeckoAppShell.sendEventToGecko(e);
if (!doSyncDraw) {
if (mDrawMode == DRAW_GLES_2 || mShowingSplashScreen)
return;
Canvas c = holder.lockCanvas();
c.drawARGB(255, 255, 255, 255);
holder.unlockCanvasAndPost(c);
return;
} else {
GeckoAppShell.scheduleRedraw();
}
} finally {
mSurfaceLock.unlock();
if (mDrawMode == DRAW_GLES_2) {
// Force a frame to be drawn before the surfaceChange returns,
// otherwise we get artifacts.
GeckoAppShell.scheduleRedraw();
GeckoAppShell.geckoEventSync();
}
}
Object syncDrawObject = null;
try {
syncDrawObject = mSyncDraws.take();
} catch (InterruptedException ie) {
Log.e(LOG_FILE_NAME, "Threw exception while getting sync draw bitmap/buffer: ", ie);
}
if (syncDrawObject != null) {
if (syncDrawObject instanceof Bitmap)
draw(holder, (Bitmap)syncDrawObject);
else
draw(holder, (ByteBuffer)syncDrawObject);
} else {
Log.e("GeckoSurfaceViewJava", "Synchronised draw object is null");
if (doSyncDraw) {
Object syncDrawObject = null;
try {
syncDrawObject = mSyncDraws.take();
} catch (InterruptedException ie) {
Log.e(LOG_FILE_NAME, "Threw exception while getting sync draw bitmap/buffer: ", ie);
}
if (syncDrawObject != null) {
if (syncDrawObject instanceof Bitmap)
draw(holder, (Bitmap)syncDrawObject);
else
draw(holder, (ByteBuffer)syncDrawObject);
} else {
Log.e("GeckoSurfaceViewJava", "Synchronised draw object is null");
}
} else if (!mShowingSplashScreen) {
// Make sure a frame is drawn before we return
// otherwise we see artifacts or a black screen
GeckoAppShell.scheduleRedraw();
GeckoAppShell.geckoEventSync();
}
}
@ -372,6 +362,10 @@ class GeckoSurfaceView
return mSoftwareBuffer;
}
public Surface getSurface() {
return getHolder().getSurface();
}
/*
* Called on Gecko thread
*/

View File

@ -106,8 +106,9 @@ AndroidBridge::Init(JNIEnv *jEnv,
mJNIEnv = nsnull;
mThread = nsnull;
mOpenedBitmapLibrary = false;
mOpenedGraphicsLibraries = false;
mHasNativeBitmapAccess = false;
mHasNativeWindowAccess = false;
mGeckoAppShellClass = (jclass) jEnv->NewGlobalRef(jGeckoAppShellClass);
@ -977,33 +978,46 @@ AndroidBridge::ExecuteNextRunnable()
}
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "leaving %s", __PRETTY_FUNCTION__);
}
void
AndroidBridge::OpenGraphicsLibraries()
{
if (!mOpenedGraphicsLibraries) {
// Try to dlopen libjnigraphics.so for direct bitmap access on
// Android 2.2+ (API level 8)
mOpenedGraphicsLibraries = true;
void *handle = dlopen("/system/lib/libjnigraphics.so", RTLD_LAZY | RTLD_LOCAL);
if (handle) {
AndroidBitmap_getInfo = (int (*)(JNIEnv *, jobject, void *))dlsym(handle, "AndroidBitmap_getInfo");
AndroidBitmap_lockPixels = (int (*)(JNIEnv *, jobject, void **))dlsym(handle, "AndroidBitmap_lockPixels");
AndroidBitmap_unlockPixels = (int (*)(JNIEnv *, jobject))dlsym(handle, "AndroidBitmap_unlockPixels");
ALOG_BRIDGE("Successfully opened libjnigraphics.so");
}
// Try to dlopen libandroid.so for and native window access on
// Android 2.3+ (API level 9)
handle = dlopen("/system/lib/libandroid.so", RTLD_LAZY | RTLD_LOCAL);
if (handle) {
ANativeWindow_fromSurface = (void* (*)(JNIEnv*, jobject))dlsym(handle, "ANativeWindow_fromSurface");
ANativeWindow_release = (void (*)(void*))dlsym(handle, "ANativeWindow_release");
ANativeWindow_setBuffersGeometry = (int (*)(void*, int, int, int)) dlsym(handle, "ANativeWindow_setBuffersGeometry");
ANativeWindow_lock = (int (*)(void*, void*, void*)) dlsym(handle, "ANativeWindow_lock");
ANativeWindow_unlockAndPost = (int (*)(void*))dlsym(handle, "ANativeWindow_unlockAndPost");
ALOG_BRIDGE("Successfully opened libandroid.so");
}
mHasNativeBitmapAccess = AndroidBitmap_getInfo && AndroidBitmap_lockPixels && AndroidBitmap_unlockPixels;
mHasNativeWindowAccess = ANativeWindow_fromSurface && ANativeWindow_release && ANativeWindow_lock && ANativeWindow_unlockAndPost;
}
}
bool
AndroidBridge::HasNativeBitmapAccess()
{
if (!mOpenedBitmapLibrary) {
// Try to dlopen libjnigraphics.so for direct bitmap access on
// Android 2.2+ (API level 8)
mOpenedBitmapLibrary = true;
void *handle = dlopen("/system/lib/libjnigraphics.so", RTLD_LAZY | RTLD_LOCAL);
if (handle == nsnull)
return false;
AndroidBitmap_getInfo = (int (*)(JNIEnv *, jobject, void *))dlsym(handle, "AndroidBitmap_getInfo");
if (AndroidBitmap_getInfo == nsnull)
return false;
AndroidBitmap_lockPixels = (int (*)(JNIEnv *, jobject, void **))dlsym(handle, "AndroidBitmap_lockPixels");
if (AndroidBitmap_lockPixels == nsnull)
return false;
AndroidBitmap_unlockPixels = (int (*)(JNIEnv *, jobject))dlsym(handle, "AndroidBitmap_unlockPixels");
if (AndroidBitmap_unlockPixels == nsnull)
return false;
ALOG_BRIDGE("Successfully opened libjnigraphics.so");
mHasNativeBitmapAccess = true;
}
OpenGraphicsLibraries();
return mHasNativeBitmapAccess;
}
@ -1057,3 +1071,92 @@ AndroidBridge::UnlockBitmap(jobject bitmap)
if ((err = AndroidBitmap_unlockPixels(JNI(), bitmap)) != 0)
ALOG_BRIDGE("AndroidBitmap_unlockPixels failed! (error %d)", err);
}
bool
AndroidBridge::HasNativeWindowAccess()
{
OpenGraphicsLibraries();
return mHasNativeWindowAccess;
}
void*
AndroidBridge::AcquireNativeWindow(jobject surface)
{
if (!HasNativeWindowAccess())
return nsnull;
return ANativeWindow_fromSurface(JNI(), surface);
}
void
AndroidBridge::ReleaseNativeWindow(void *window)
{
if (!window)
return;
ANativeWindow_release(window);
}
bool
AndroidBridge::SetNativeWindowFormat(void *window, int format)
{
return ANativeWindow_setBuffersGeometry(window, 0, 0, format) == 0;
}
bool
AndroidBridge::LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride)
{
/* Copied from native_window.h in Android NDK (platform-9) */
typedef struct ANativeWindow_Buffer {
// The number of pixels that are show horizontally.
int32_t width;
// The number of pixels that are shown vertically.
int32_t height;
// The number of *pixels* that a line in the buffer takes in
// memory. This may be >= width.
int32_t stride;
// The format of the buffer. One of WINDOW_FORMAT_*
int32_t format;
// The actual bits.
void* bits;
// Do not touch.
uint32_t reserved[6];
} ANativeWindow_Buffer;
int err;
ANativeWindow_Buffer buffer;
*bits = NULL;
*width = *height = *format = 0;
if ((err = ANativeWindow_lock(window, (void*)&buffer, NULL)) != 0) {
ALOG_BRIDGE("ANativeWindow_lock failed! (error %d)", err);
return false;
}
*bits = (unsigned char*)buffer.bits;
*width = buffer.width;
*height = buffer.height;
*format = buffer.format;
*stride = buffer.stride;
return true;
}
bool
AndroidBridge::UnlockWindow(void* window)
{
int err;
if ((err = ANativeWindow_unlockAndPost(window)) != 0) {
ALOG_BRIDGE("ANativeWindow_unlockAndPost failed! (error %d)", err);
return false;
}
return true;
}

View File

@ -267,6 +267,22 @@ public:
void ExecuteNextRunnable();
/* Copied from Android's native_window.h in newer (platform 9) NDK */
enum {
WINDOW_FORMAT_RGBA_8888 = 1,
WINDOW_FORMAT_RGBX_8888 = 2,
WINDOW_FORMAT_RGB_565 = 4,
};
bool HasNativeWindowAccess();
void *AcquireNativeWindow(jobject surface);
void ReleaseNativeWindow(void *window);
bool SetNativeWindowFormat(void *window, int format);
bool LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride);
bool UnlockWindow(void *window);
protected:
static AndroidBridge *sBridge;
@ -288,8 +304,11 @@ protected:
void EnsureJNIThread();
bool mOpenedBitmapLibrary;
bool mOpenedGraphicsLibraries;
void OpenGraphicsLibraries();
bool mHasNativeBitmapAccess;
bool mHasNativeWindowAccess;
nsCOMArray<nsIRunnable> mRunnableQueue;
@ -346,6 +365,13 @@ protected:
int (* AndroidBitmap_getInfo)(JNIEnv *env, jobject bitmap, void *info);
int (* AndroidBitmap_lockPixels)(JNIEnv *env, jobject bitmap, void **buffer);
int (* AndroidBitmap_unlockPixels)(JNIEnv *env, jobject bitmap);
void* (*ANativeWindow_fromSurface)(JNIEnv *env, jobject surface);
void (*ANativeWindow_release)(void *window);
int (*ANativeWindow_setBuffersGeometry)(void *window, int width, int height, int format);
int (* ANativeWindow_lock)(void *window, void *outBuffer, void *inOutDirtyBounds);
int (* ANativeWindow_unlockAndPost)(void *window);
};
}

View File

@ -109,6 +109,7 @@ jmethodID AndroidGeckoSurfaceView::jDraw2DBitmapMethod = 0;
jmethodID AndroidGeckoSurfaceView::jDraw2DBufferMethod = 0;
jmethodID AndroidGeckoSurfaceView::jGetSoftwareDrawBitmapMethod = 0;
jmethodID AndroidGeckoSurfaceView::jGetSoftwareDrawBufferMethod = 0;
jmethodID AndroidGeckoSurfaceView::jGetSurfaceMethod = 0;
jmethodID AndroidGeckoSurfaceView::jGetHolderMethod = 0;
#define JNI() (AndroidBridge::JNI())
@ -183,6 +184,7 @@ AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(JNIEnv *jEnv)
jEndDrawingMethod = getMethod("endDrawing", "()V");
jDraw2DBitmapMethod = getMethod("draw2D", "(Landroid/graphics/Bitmap;II)V");
jDraw2DBufferMethod = getMethod("draw2D", "(Ljava/nio/ByteBuffer;I)V");
jGetSurfaceMethod = getMethod("getSurface", "()Landroid/view/Surface;");
jGetHolderMethod = getMethod("getHolder", "()Landroid/view/SurfaceHolder;");
}
@ -516,6 +518,12 @@ AndroidGeckoSurfaceView::GetSoftwareDrawBuffer()
return JNI()->CallObjectMethod(wrapped_obj, jGetSoftwareDrawBufferMethod);
}
jobject
AndroidGeckoSurfaceView::GetSurface()
{
return JNI()->CallObjectMethod(wrapped_obj, jGetSurfaceMethod);
}
jobject
AndroidGeckoSurfaceView::GetSurfaceHolder()
{

View File

@ -175,6 +175,8 @@ public:
void Draw2D(jobject bitmap, int width, int height);
void Draw2D(jobject buffer, int stride);
jobject GetSurface();
// must have a JNI local frame when calling this,
// and you'd better know what you're doing
jobject GetSurfaceHolder();
@ -187,6 +189,7 @@ protected:
static jmethodID jDraw2DBufferMethod;
static jmethodID jGetSoftwareDrawBitmapMethod;
static jmethodID jGetSoftwareDrawBufferMethod;
static jmethodID jGetSurfaceMethod;
static jmethodID jGetHolderMethod;
};

View File

@ -129,6 +129,7 @@ static nsRefPtr<gl::GLContext> sGLContext;
static bool sFailedToCreateGLContext = false;
static bool sValidSurface;
static bool sSurfaceExists = false;
static void *sNativeWindow = nsnull;
// Multitouch swipe thresholds in inches
static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
@ -846,12 +847,27 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
case AndroidGeckoEvent::SURFACE_CREATED:
sSurfaceExists = true;
if (AndroidBridge::Bridge()->HasNativeWindowAccess()) {
AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());
jobject surface = sview.GetSurface();
if (surface) {
sNativeWindow = AndroidBridge::Bridge()->AcquireNativeWindow(surface);
if (sNativeWindow) {
AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, AndroidBridge::WINDOW_FORMAT_RGB_565);
}
}
}
break;
case AndroidGeckoEvent::SURFACE_DESTROYED:
if (sGLContext && sValidSurface) {
sGLContext->ReleaseSurface();
}
if (sNativeWindow) {
AndroidBridge::Bridge()->ReleaseNativeWindow(sNativeWindow);
sNativeWindow = nsnull;
}
sSurfaceExists = false;
sValidSurface = false;
break;
@ -1002,7 +1018,35 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
AndroidBridge::Bridge()->HideProgressDialogOnce();
if (GetLayerManager(nsnull)->GetBackendType() == LayerManager::LAYERS_BASIC) {
if (AndroidBridge::Bridge()->HasNativeBitmapAccess()) {
if (sNativeWindow) {
unsigned char *bits;
int width, height, format, stride;
if (!AndroidBridge::Bridge()->LockWindow(sNativeWindow, &bits, &width, &height, &format, &stride)) {
ALOG("failed to lock buffer - skipping draw");
return;
}
if (!bits || format != AndroidBridge::WINDOW_FORMAT_RGB_565 ||
width != mBounds.width || height != mBounds.height) {
ALOG("surface is not expected dimensions or format - skipping draw");
AndroidBridge::Bridge()->UnlockWindow(sNativeWindow);
return;
}
nsRefPtr<gfxImageSurface> targetSurface =
new gfxImageSurface(bits,
gfxIntSize(mBounds.width, mBounds.height),
stride * 2,
gfxASurface::ImageFormatRGB16_565);
if (targetSurface->CairoStatus()) {
ALOG("### Failed to create a valid surface from the bitmap");
} else {
DrawTo(targetSurface);
}
AndroidBridge::Bridge()->UnlockWindow(sNativeWindow);
} else if (AndroidBridge::Bridge()->HasNativeBitmapAccess()) {
jobject bitmap = sview.GetSoftwareDrawBitmap();
if (!bitmap) {
ALOG("no bitmap to draw into - skipping draw");