Index: android_webview/native/aw_contents.cc |
diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc |
index f170e4927ac7041ee9ce4006daaf45e2d441da1d..a7ae2e189a4530057e01b5271d0ed00c697cb947 100644 |
--- a/android_webview/native/aw_contents.cc |
+++ b/android_webview/native/aw_contents.cc |
@@ -4,6 +4,7 @@ |
#include "android_webview/native/aw_contents.h" |
+#include <android/bitmap.h> |
#include <sys/system_properties.h> |
#include "android_webview/browser/aw_browser_context.h" |
@@ -39,10 +40,10 @@ |
#include "content/public/common/ssl_status.h" |
#include "jni/AwContents_jni.h" |
#include "net/base/x509_certificate.h" |
-#include "skia/ext/refptr.h" |
#include "third_party/skia/include/core/SkBitmap.h" |
#include "third_party/skia/include/core/SkCanvas.h" |
#include "third_party/skia/include/core/SkDevice.h" |
+#include "third_party/skia/include/core/SkGraphics.h" |
#include "third_party/skia/include/core/SkPicture.h" |
#include "ui/gfx/android/java_bitmap.h" |
#include "ui/gfx/transform.h" |
@@ -82,6 +83,50 @@ static void DrawGLFunction(int view_context, |
reinterpret_cast<android_webview::AwContents*>(view_context)->DrawGL( |
draw_info); |
} |
+ |
+typedef base::Callback<bool(SkCanvas*)> RenderMethod; |
+ |
+static bool RasterizeIntoBitmap(JNIEnv* env, |
+ jobject jbitmap, |
+ int scroll_x, |
+ int scroll_y, |
+ const RenderMethod& renderer) { |
+ DCHECK(jbitmap); |
+ |
+ AndroidBitmapInfo bitmap_info; |
+ if (AndroidBitmap_getInfo(env, jbitmap, &bitmap_info) < 0) { |
+ LOG(WARNING) << "Error getting java bitmap info."; |
+ return false; |
+ } |
+ |
+ void* pixels = NULL; |
+ if (AndroidBitmap_lockPixels(env, jbitmap, &pixels) < 0) { |
+ LOG(WARNING) << "Error locking java bitmap pixels."; |
+ return false; |
+ } |
+ |
+ bool succeeded = false; |
+ { |
+ SkBitmap bitmap; |
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, |
+ bitmap_info.width, |
+ bitmap_info.height, |
+ bitmap_info.stride); |
+ bitmap.setPixels(pixels); |
+ |
+ SkDevice device(bitmap); |
+ SkCanvas canvas(&device); |
+ canvas.translate(-scroll_x, -scroll_y); |
+ succeeded = renderer.Run(&canvas); |
+ } |
+ |
+ if (AndroidBitmap_unlockPixels(env, jbitmap) < 0) { |
+ LOG(WARNING) << "Error unlocking java bitmap pixels."; |
+ return false; |
+ } |
+ |
+ return succeeded; |
+} |
} |
namespace android_webview { |
@@ -89,6 +134,7 @@ namespace android_webview { |
namespace { |
AwDrawSWFunctionTable* g_draw_sw_functions = NULL; |
+bool g_is_skia_version_compatible = false; |
const void* kAwContentsUserDataKey = &kAwContentsUserDataKey; |
@@ -124,6 +170,7 @@ AwContents::AwContents(JNIEnv* env, |
view_visible_(false), |
compositor_visible_(false), |
is_composite_pending_(false), |
+ on_new_picture_mode_(kOnNewPictureDisabled), |
last_frame_context_(NULL) { |
RendererPictureMap::CreateInstance(); |
android_webview::AwBrowserDependencyFactory* dependency_factory = |
@@ -174,7 +221,8 @@ void AwContents::DrawGL(AwDrawGLInfo* draw_info) { |
TRACE_EVENT0("AwContents", "AwContents::DrawGL"); |
- if (!scissor_clip_layer_ || draw_info->mode == AwDrawGLInfo::kModeProcess) |
+ if (view_size_.IsEmpty() || !scissor_clip_layer_ || |
+ draw_info->mode == AwDrawGLInfo::kModeProcess) |
return; |
DCHECK_EQ(draw_info->mode, AwDrawGLInfo::kModeDraw); |
@@ -388,20 +436,38 @@ void AwContents::DrawGL(AwDrawGLInfo* draw_info) { |
// --------------------------------------------------------------------------- |
} |
-bool AwContents::DrawSW(JNIEnv* env, jobject obj, jobject java_canvas) { |
- skia::RefPtr<SkPicture> picture = |
- RendererPictureMap::GetInstance()->GetRendererPicture( |
- web_contents_->GetRoutingID()); |
- if (!picture) |
- return false; |
+bool AwContents::DrawSW(JNIEnv* env, |
+ jobject obj, |
+ jobject java_canvas, |
+ jint clip_x, |
+ jint clip_y, |
+ jint clip_w, |
+ jint clip_h) { |
+ TRACE_EVENT0("AwContents", "AwContents::DrawSW"); |
+ |
+ if (clip_w <= 0 || clip_h <= 0) |
+ return true; |
AwPixelInfo* pixels; |
+ |
+ // Render into an auxiliary bitmap if pixel info is not available. |
if (!g_draw_sw_functions || |
(pixels = g_draw_sw_functions->access_pixels(env, java_canvas)) == NULL) { |
- // TODO(joth): Fall back to slow path rendering via temporary bitmap. |
- return false; |
+ ScopedJavaLocalRef<jobject> jbitmap(Java_AwContents_createBitmap( |
+ env, clip_w, clip_h)); |
+ if (!jbitmap.obj()) |
+ return false; |
+ |
+ if (!RasterizeIntoBitmap(env, jbitmap.obj(), clip_x, clip_y, |
+ base::Bind(&AwContents::RenderSW, base::Unretained(this)))) |
+ return false; |
+ |
+ Java_AwContents_drawBitmapIntoCanvas(env, jbitmap.obj(), java_canvas); |
+ return true; |
} |
+ // Draw in a SkCanvas built over the pixel information. |
+ bool succeeded = false; |
{ |
SkBitmap bitmap; |
bitmap.setConfig(static_cast<SkBitmap::Config>(pixels->config), |
@@ -425,11 +491,11 @@ bool AwContents::DrawSW(JNIEnv* env, jobject obj, jobject java_canvas) { |
clip.setRect(SkIRect::MakeWH(pixels->width, pixels->height)); |
} |
- picture->draw(&canvas); |
+ succeeded = RenderSW(&canvas); |
} |
g_draw_sw_functions->release_pixels(pixels); |
- return true; |
+ return succeeded; |
} |
jint AwContents::GetWebContents(JNIEnv* env, jobject obj) { |
@@ -472,6 +538,11 @@ void AwContents::Destroy(JNIEnv* env, jobject obj) { |
void SetAwDrawSWFunctionTable(JNIEnv* env, jclass, jint function_table) { |
g_draw_sw_functions = |
reinterpret_cast<AwDrawSWFunctionTable*>(function_table); |
+ // TODO(leandrogracia): uncomment once the glue layer implements this method. |
+ //g_is_skia_version_compatible = |
+ // g_draw_sw_functions->is_skia_version_compatible(&SkGraphics::GetVersion); |
+ LOG_IF(WARNING, !g_is_skia_version_compatible) << |
+ "Skia native versions are not compatible."; |
} |
// static |
@@ -755,7 +826,13 @@ void AwContents::Invalidate() { |
if (obj.is_null()) |
return; |
- Java_AwContents_invalidate(env, obj.obj()); |
+ if (view_visible_) |
+ Java_AwContents_invalidate(env, obj.obj()); |
+ |
+ // When not in invalidation-only mode onNewPicture will be triggered |
+ // from the OnPictureUpdated callback. |
+ if (on_new_picture_mode_ == kOnNewPictureInvalidationOnly) |
+ Java_AwContents_onNewPicture(env, obj.obj(), NULL); |
} |
void AwContents::SetCompositorVisibility(bool visible) { |
@@ -907,14 +984,105 @@ jint AwContents::ReleasePopupWebContents(JNIEnv* env, jobject obj) { |
return reinterpret_cast<jint>(pending_contents_.release()); |
} |
+ScopedJavaLocalRef<jobject> AwContents::CapturePicture(JNIEnv* env, |
+ jobject obj) { |
+ skia::RefPtr<SkPicture> picture = GetLastCapturedPicture(); |
+ if (!picture || !g_draw_sw_functions) |
+ return ScopedJavaLocalRef<jobject>(); |
+ |
+ if (g_is_skia_version_compatible) |
+ return ScopedJavaLocalRef<jobject>(env, |
+ g_draw_sw_functions->create_picture(env, picture->clone())); |
+ |
+ // If Skia versions are not compatible, workaround it by rasterizing the |
+ // picture into a bitmap and drawing it into a new Java picture. |
+ ScopedJavaLocalRef<jobject> jbitmap(Java_AwContents_createBitmap( |
+ env, picture->width(), picture->height())); |
+ if (!jbitmap.obj()) |
+ return ScopedJavaLocalRef<jobject>(); |
+ |
+ if (!RasterizeIntoBitmap(env, jbitmap.obj(), 0, 0, |
+ base::Bind(&AwContents::RenderPicture, base::Unretained(this)))) |
+ return ScopedJavaLocalRef<jobject>(); |
+ |
+ return Java_AwContents_recordBitmapIntoPicture(env, jbitmap.obj()); |
+} |
+ |
+bool AwContents::RenderSW(SkCanvas* canvas) { |
+ // TODO(leandrogracia): once Ubercompositor is ready and we support software |
+ // rendering mode, we should avoid this as much as we can, ideally always. |
+ // This includes finding a proper replacement for onDraw calls in hardware |
+ // mode with software canvases. http://crbug.com/170086. |
+ return RenderPicture(canvas); |
+} |
+ |
+bool AwContents::RenderPicture(SkCanvas* canvas) { |
+ skia::RefPtr<SkPicture> picture = GetLastCapturedPicture(); |
+ if (!picture) |
+ return false; |
+ |
+ picture->draw(canvas); |
+ return true; |
+} |
+ |
+void AwContents::EnableOnNewPicture(JNIEnv* env, |
+ jobject obj, |
+ jboolean enabled, |
+ jboolean invalidation_only) { |
+ if (enabled) { |
+ on_new_picture_mode_ = invalidation_only ? kOnNewPictureInvalidationOnly : |
+ kOnNewPictureEnabled; |
+ } else { |
+ on_new_picture_mode_ = kOnNewPictureDisabled; |
+ } |
+ |
+ // If onNewPicture is triggered only on invalidation do not capture |
+ // pictures on every new frame. |
+ if (on_new_picture_mode_ == kOnNewPictureInvalidationOnly) |
+ enabled = false; |
+ |
+ // TODO(leandrogracia): when SW rendering uses the compositor rather than |
+ // picture rasterization, send update the renderer side with the correct |
+ // listener state. (For now, we always leave render picture listener enabled). |
+ // render_view_host_ext_->EnableCapturePictureCallback(enabled); |
+} |
+ |
void AwContents::OnPictureUpdated(int process_id, int render_view_id) { |
CHECK_EQ(web_contents_->GetRenderProcessHost()->GetID(), process_id); |
if (render_view_id != web_contents_->GetRoutingID()) |
return; |
+ // TODO(leandrogracia): this can be made unconditional once software rendering |
+ // uses Ubercompositor. Until then this path is required for SW invalidations. |
+ if (on_new_picture_mode_ == kOnNewPictureEnabled) { |
+ JNIEnv* env = AttachCurrentThread(); |
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
+ if (!obj.is_null()) { |
+ ScopedJavaLocalRef<jobject> picture = CapturePicture(env, obj.obj()); |
+ Java_AwContents_onNewPicture(env, obj.obj(), picture.obj()); |
+ } |
+ } |
+ |
// TODO(leandrogracia): delete when sw rendering uses Ubercompositor. |
// Invalidation should be provided by the compositor only. |
Invalidate(); |
} |
+skia::RefPtr<SkPicture> AwContents::GetLastCapturedPicture() { |
+ // Use the latest available picture if the listener callback is enabled. |
+ skia::RefPtr<SkPicture> picture; |
+ if (on_new_picture_mode_ == kOnNewPictureEnabled) |
+ picture = RendererPictureMap::GetInstance()->GetRendererPicture( |
+ web_contents_->GetRoutingID()); |
+ |
+ // If not available or not in listener mode get it synchronously. |
+ if (!picture) { |
+ render_view_host_ext_->CapturePictureSync(); |
+ picture = RendererPictureMap::GetInstance()->GetRendererPicture( |
+ web_contents_->GetRoutingID()); |
+ } |
+ |
+ return picture; |
+} |
+ |
} // namespace android_webview |