| Index: android_webview/browser/browser_view_renderer_impl.cc
 | 
| diff --git a/android_webview/browser/browser_view_renderer_impl.cc b/android_webview/browser/browser_view_renderer_impl.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..250cdb0a1292c1d5f38872b015726c06b529939e
 | 
| --- /dev/null
 | 
| +++ b/android_webview/browser/browser_view_renderer_impl.cc
 | 
| @@ -0,0 +1,596 @@
 | 
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "android_webview/browser/browser_view_renderer_impl.h"
 | 
| +
 | 
| +#include <android/bitmap.h>
 | 
| +#include <sys/system_properties.h>
 | 
| +
 | 
| +#include "android_webview/common/renderer_picture_map.h"
 | 
| +#include "android_webview/public/browser/draw_gl.h"
 | 
| +#include "android_webview/public/browser/draw_sw.h"
 | 
| +#include "base/android/jni_android.h"
 | 
| +#include "base/debug/trace_event.h"
 | 
| +#include "base/logging.h"
 | 
| +#include "cc/layer.h"
 | 
| +#include "content/public/browser/android/content_view_core.h"
 | 
| +#include "content/public/browser/render_process_host.h"
 | 
| +#include "content/public/browser/web_contents.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 "ui/gfx/size.h"
 | 
| +#include "ui/gfx/transform.h"
 | 
| +#include "ui/gl/gl_bindings.h"
 | 
| +
 | 
| +// TODO(leandrogracia): remove when crbug.com/164140 is closed.
 | 
| +// Borrowed from gl2ext.h. Cannot be included due to conflicts with
 | 
| +// gl_bindings.h and the EGL library methods (eglGetCurrentContext).
 | 
| +#ifndef GL_TEXTURE_EXTERNAL_OES
 | 
| +#define GL_TEXTURE_EXTERNAL_OES 0x8D65
 | 
| +#endif
 | 
| +
 | 
| +#ifndef GL_TEXTURE_BINDING_EXTERNAL_OES
 | 
| +#define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67
 | 
| +#endif
 | 
| +
 | 
| +using base::android::AttachCurrentThread;
 | 
| +using base::android::JavaRef;
 | 
| +using base::android::ScopedJavaLocalRef;
 | 
| +using content::Compositor;
 | 
| +using content::ContentViewCore;
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +// Provides software rendering functions from the Android glue layer.
 | 
| +// Allows preventing extra copies of data when rendering.
 | 
| +AwDrawSWFunctionTable* g_sw_draw_functions = NULL;
 | 
| +
 | 
| +// Tells if the Skia library versions in Android and Chromium are compatible.
 | 
| +// If they are then it's possible to pass Skia objects like SkPictures to the
 | 
| +// Android glue layer via the SW rendering functions.
 | 
| +// If they are not, then additional copies and rasterizations are required
 | 
| +// as a fallback mechanism, which will have an important performance impact.
 | 
| +bool g_is_skia_version_compatible = false;
 | 
| +
 | 
| +typedef base::Callback<bool(SkCanvas*)> RenderMethod;
 | 
| +
 | 
| +static bool RasterizeIntoBitmap(JNIEnv* env,
 | 
| +                                const JavaRef<jobject>& jbitmap,
 | 
| +                                int scroll_x,
 | 
| +                                int scroll_y,
 | 
| +                                const RenderMethod& renderer) {
 | 
| +  DCHECK(jbitmap.obj());
 | 
| +
 | 
| +  AndroidBitmapInfo bitmap_info;
 | 
| +  if (AndroidBitmap_getInfo(env, jbitmap.obj(), &bitmap_info) < 0) {
 | 
| +    LOG(ERROR) << "Error getting java bitmap info.";
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  void* pixels = NULL;
 | 
| +  if (AndroidBitmap_lockPixels(env, jbitmap.obj(), &pixels) < 0) {
 | 
| +    LOG(ERROR) << "Error locking java bitmap pixels.";
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  bool succeeded;
 | 
| +  {
 | 
| +    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.obj()) < 0) {
 | 
| +    LOG(ERROR) << "Error unlocking java bitmap pixels.";
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  return succeeded;
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +namespace android_webview {
 | 
| +
 | 
| +// static
 | 
| +BrowserViewRendererImpl* BrowserViewRendererImpl::Create(
 | 
| +    BrowserViewRenderer::Client* client,
 | 
| +    JavaHelper* java_helper) {
 | 
| +  return new BrowserViewRendererImpl(client, java_helper);
 | 
| +}
 | 
| +
 | 
| +BrowserViewRendererImpl::BrowserViewRendererImpl(
 | 
| +    BrowserViewRenderer::Client* client,
 | 
| +    JavaHelper* java_helper)
 | 
| +    : client_(client),
 | 
| +      java_helper_(java_helper),
 | 
| +      ALLOW_THIS_IN_INITIALIZER_LIST(compositor_(Compositor::Create(this))),
 | 
| +      view_clip_layer_(cc::Layer::create()),
 | 
| +      transform_layer_(cc::Layer::create()),
 | 
| +      scissor_clip_layer_(cc::Layer::create()),
 | 
| +      view_visible_(false),
 | 
| +      compositor_visible_(false),
 | 
| +      is_composite_pending_(false),
 | 
| +      dpi_scale_(1.0f),
 | 
| +      on_new_picture_mode_(kOnNewPictureDisabled),
 | 
| +      last_frame_context_(NULL),
 | 
| +      web_contents_(NULL) {
 | 
| +  DCHECK(java_helper);
 | 
| +
 | 
| +  // Define the view hierarchy.
 | 
| +  transform_layer_->addChild(view_clip_layer_);
 | 
| +  scissor_clip_layer_->addChild(transform_layer_);
 | 
| +  compositor_->SetRootLayer(scissor_clip_layer_);
 | 
| +
 | 
| +  RendererPictureMap::CreateInstance();
 | 
| +}
 | 
| +
 | 
| +BrowserViewRendererImpl::~BrowserViewRendererImpl() {
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +void BrowserViewRendererImpl::SetAwDrawSWFunctionTable(
 | 
| +    AwDrawSWFunctionTable* table) {
 | 
| +  g_sw_draw_functions = table;
 | 
| +  g_is_skia_version_compatible =
 | 
| +      g_sw_draw_functions->is_skia_version_compatible(&SkGraphics::GetVersion);
 | 
| +  LOG_IF(WARNING, !g_is_skia_version_compatible)
 | 
| +      << "Skia versions are not compatible, rendering performance will suffer.";
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::SetContents(ContentViewCore* content_view_core) {
 | 
| +  dpi_scale_ = content_view_core->GetDpiScale();
 | 
| +  web_contents_ = content_view_core->GetWebContents();
 | 
| +  if (!view_renderer_host_)
 | 
| +    view_renderer_host_.reset(new ViewRendererHost(web_contents_, this));
 | 
| +  else
 | 
| +    view_renderer_host_->Observe(web_contents_);
 | 
| +
 | 
| +  view_clip_layer_->removeAllChildren();
 | 
| +  view_clip_layer_->addChild(content_view_core->GetLayer());
 | 
| +  Invalidate();
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::DrawGL(AwDrawGLInfo* draw_info) {
 | 
| +  TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::DrawGL");
 | 
| +
 | 
| +  if (view_size_.IsEmpty() || !scissor_clip_layer_ ||
 | 
| +      draw_info->mode == AwDrawGLInfo::kModeProcess)
 | 
| +    return;
 | 
| +
 | 
| +  DCHECK_EQ(draw_info->mode, AwDrawGLInfo::kModeDraw);
 | 
| +
 | 
| +  SetCompositorVisibility(view_visible_);
 | 
| +  if (!compositor_visible_)
 | 
| +    return;
 | 
| +
 | 
| +  // TODO(leandrogracia): remove when crbug.com/164140 is closed.
 | 
| +  // ---------------------------------------------------------------------------
 | 
| +  GLint texture_external_oes_binding;
 | 
| +  glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_external_oes_binding);
 | 
| +
 | 
| +  GLint vertex_array_buffer_binding;
 | 
| +  glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vertex_array_buffer_binding);
 | 
| +
 | 
| +  GLint index_array_buffer_binding;
 | 
| +  glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &index_array_buffer_binding);
 | 
| +
 | 
| +  GLint pack_alignment;
 | 
| +  glGetIntegerv(GL_PACK_ALIGNMENT, &pack_alignment);
 | 
| +
 | 
| +  GLint unpack_alignment;
 | 
| +  glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment);
 | 
| +
 | 
| +  struct {
 | 
| +    GLint enabled;
 | 
| +    GLint size;
 | 
| +    GLint type;
 | 
| +    GLint normalized;
 | 
| +    GLint stride;
 | 
| +    GLvoid* pointer;
 | 
| +  } vertex_attrib[3];
 | 
| +
 | 
| +  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(vertex_attrib); ++i) {
 | 
| +    glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED,
 | 
| +                        &vertex_attrib[i].enabled);
 | 
| +    glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE,
 | 
| +                        &vertex_attrib[i].size);
 | 
| +    glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE,
 | 
| +                        &vertex_attrib[i].type);
 | 
| +    glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED,
 | 
| +                        &vertex_attrib[i].normalized);
 | 
| +    glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE,
 | 
| +                        &vertex_attrib[i].stride);
 | 
| +    glGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER,
 | 
| +                        &vertex_attrib[i].pointer);
 | 
| +  }
 | 
| +
 | 
| +  GLboolean depth_test;
 | 
| +  glGetBooleanv(GL_DEPTH_TEST, &depth_test);
 | 
| +
 | 
| +  GLboolean cull_face;
 | 
| +  glGetBooleanv(GL_CULL_FACE, &cull_face);
 | 
| +
 | 
| +  GLboolean color_mask[4];
 | 
| +  glGetBooleanv(GL_COLOR_WRITEMASK, color_mask);
 | 
| +
 | 
| +  GLboolean blend_enabled;
 | 
| +  glGetBooleanv(GL_BLEND, &blend_enabled);
 | 
| +
 | 
| +  GLint blend_src_rgb;
 | 
| +  glGetIntegerv(GL_BLEND_SRC_RGB, &blend_src_rgb);
 | 
| +
 | 
| +  GLint blend_src_alpha;
 | 
| +  glGetIntegerv(GL_BLEND_SRC_ALPHA, &blend_src_alpha);
 | 
| +
 | 
| +  GLint blend_dest_rgb;
 | 
| +  glGetIntegerv(GL_BLEND_DST_RGB, &blend_dest_rgb);
 | 
| +
 | 
| +  GLint blend_dest_alpha;
 | 
| +  glGetIntegerv(GL_BLEND_DST_ALPHA, &blend_dest_alpha);
 | 
| +
 | 
| +  GLint active_texture;
 | 
| +  glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
 | 
| +
 | 
| +  GLint viewport[4];
 | 
| +  glGetIntegerv(GL_VIEWPORT, viewport);
 | 
| +
 | 
| +  GLboolean scissor_test;
 | 
| +  glGetBooleanv(GL_SCISSOR_TEST, &scissor_test);
 | 
| +
 | 
| +  GLint scissor_box[4];
 | 
| +  glGetIntegerv(GL_SCISSOR_BOX, scissor_box);
 | 
| +
 | 
| +  GLint current_program;
 | 
| +  glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program);
 | 
| +  // ---------------------------------------------------------------------------
 | 
| +
 | 
| +  // We need to watch if the current Android context has changed and enforce
 | 
| +  // a clean-up in the compositor.
 | 
| +  EGLContext current_context = eglGetCurrentContext();
 | 
| +  if (!current_context) {
 | 
| +    LOG(WARNING) << "No current context attached. Skipping composite.";
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  if (last_frame_context_ != current_context) {
 | 
| +    if (last_frame_context_)
 | 
| +      ResetCompositor();
 | 
| +    last_frame_context_ = current_context;
 | 
| +  }
 | 
| +
 | 
| +  compositor_->SetWindowBounds(gfx::Size(draw_info->width, draw_info->height));
 | 
| +
 | 
| +  if (draw_info->is_layer) {
 | 
| +    // When rendering into a separate layer no view clipping, transform,
 | 
| +    // scissoring or background transparency need to be handled.
 | 
| +    // The Android framework will composite us afterwards.
 | 
| +    compositor_->SetHasTransparentBackground(false);
 | 
| +    view_clip_layer_->setMasksToBounds(false);
 | 
| +    transform_layer_->setTransform(gfx::Transform());
 | 
| +    scissor_clip_layer_->setMasksToBounds(false);
 | 
| +    scissor_clip_layer_->setPosition(gfx::PointF());
 | 
| +    scissor_clip_layer_->setBounds(gfx::Size());
 | 
| +    scissor_clip_layer_->setSublayerTransform(gfx::Transform());
 | 
| +
 | 
| +  } else {
 | 
| +    compositor_->SetHasTransparentBackground(true);
 | 
| +
 | 
| +    gfx::Rect clip_rect(draw_info->clip_left, draw_info->clip_top,
 | 
| +                        draw_info->clip_right - draw_info->clip_left,
 | 
| +                        draw_info->clip_bottom - draw_info->clip_top);
 | 
| +
 | 
| +    scissor_clip_layer_->setPosition(clip_rect.origin());
 | 
| +    scissor_clip_layer_->setBounds(clip_rect.size());
 | 
| +    scissor_clip_layer_->setMasksToBounds(true);
 | 
| +
 | 
| +    // The compositor clipping architecture enforces us to have the clip layer
 | 
| +    // as an ancestor of the area we want to clip, but this makes the transform
 | 
| +    // become relative to the clip area rather than the full surface. The clip
 | 
| +    // position offset needs to be undone before applying the transform.
 | 
| +    gfx::Transform undo_clip_position;
 | 
| +    undo_clip_position.Translate(-clip_rect.x(), -clip_rect.y());
 | 
| +    scissor_clip_layer_->setSublayerTransform(undo_clip_position);
 | 
| +
 | 
| +    gfx::Transform transform;
 | 
| +    transform.matrix().setColMajorf(draw_info->transform);
 | 
| +
 | 
| +    // The scrolling values of the Android Framework affect the transformation
 | 
| +    // matrix. This needs to be undone to let the compositor handle scrolling.
 | 
| +    // TODO(leandrogracia): when scrolling becomes synchronous we should undo
 | 
| +    // or override the translation in the compositor, not the one coming from
 | 
| +    // the Android View System, as it could be out of bounds for overscrolling.
 | 
| +    transform.Translate(hw_rendering_scroll_.x(), hw_rendering_scroll_.y());
 | 
| +    transform_layer_->setTransform(transform);
 | 
| +
 | 
| +    view_clip_layer_->setMasksToBounds(true);
 | 
| +  }
 | 
| +
 | 
| +  compositor_->Composite();
 | 
| +  is_composite_pending_ = false;
 | 
| +
 | 
| +  // TODO(leandrogracia): remove when crbug.com/164140 is closed.
 | 
| +  // ---------------------------------------------------------------------------
 | 
| +  char no_gl_restore_prop[PROP_VALUE_MAX];
 | 
| +  __system_property_get("webview.chromium_no_gl_restore", no_gl_restore_prop);
 | 
| +  if (!strcmp(no_gl_restore_prop, "true")) {
 | 
| +    LOG(WARNING) << "Android GL functor not restoring the previous GL state.";
 | 
| +  } else {
 | 
| +    glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_external_oes_binding);
 | 
| +    glBindBuffer(GL_ARRAY_BUFFER, vertex_array_buffer_binding);
 | 
| +    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_buffer_binding);
 | 
| +
 | 
| +    glPixelStorei(GL_PACK_ALIGNMENT, pack_alignment);
 | 
| +    glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
 | 
| +
 | 
| +    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(vertex_attrib); ++i) {
 | 
| +      glVertexAttribPointer(i, vertex_attrib[i].size,
 | 
| +          vertex_attrib[i].type, vertex_attrib[i].normalized,
 | 
| +          vertex_attrib[i].stride, vertex_attrib[i].pointer);
 | 
| +
 | 
| +      if (vertex_attrib[i].enabled)
 | 
| +        glEnableVertexAttribArray(i);
 | 
| +      else
 | 
| +        glDisableVertexAttribArray(i);
 | 
| +    }
 | 
| +
 | 
| +    if (depth_test)
 | 
| +      glEnable(GL_DEPTH_TEST);
 | 
| +    else
 | 
| +      glDisable(GL_DEPTH_TEST);
 | 
| +
 | 
| +    if (cull_face)
 | 
| +      glEnable(GL_CULL_FACE);
 | 
| +    else
 | 
| +      glDisable(GL_CULL_FACE);
 | 
| +
 | 
| +    glColorMask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
 | 
| +
 | 
| +    if (blend_enabled)
 | 
| +      glEnable(GL_BLEND);
 | 
| +    else
 | 
| +      glDisable(GL_BLEND);
 | 
| +
 | 
| +    glBlendFuncSeparate(blend_src_rgb, blend_dest_rgb,
 | 
| +                        blend_src_alpha, blend_dest_alpha);
 | 
| +
 | 
| +    glActiveTexture(active_texture);
 | 
| +
 | 
| +    glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
 | 
| +
 | 
| +    if (scissor_test)
 | 
| +      glEnable(GL_SCISSOR_TEST);
 | 
| +    else
 | 
| +      glDisable(GL_SCISSOR_TEST);
 | 
| +
 | 
| +    glScissor(scissor_box[0], scissor_box[1], scissor_box[2],
 | 
| +              scissor_box[3]);
 | 
| +
 | 
| +    glUseProgram(current_program);
 | 
| +  }
 | 
| +  // ---------------------------------------------------------------------------
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::SetScrollForHWFrame(int x, int y) {
 | 
| +  hw_rendering_scroll_ = gfx::Point(x, y);
 | 
| +}
 | 
| +
 | 
| +bool BrowserViewRendererImpl::DrawSW(jobject java_canvas,
 | 
| +                                     const gfx::Rect& clip) {
 | 
| +  TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::DrawSW");
 | 
| +
 | 
| +  if (clip.IsEmpty())
 | 
| +    return true;
 | 
| +
 | 
| +  AwPixelInfo* pixels;
 | 
| +  JNIEnv* env = AttachCurrentThread();
 | 
| +
 | 
| +  // Render into an auxiliary bitmap if pixel info is not available.
 | 
| +  if (!g_sw_draw_functions ||
 | 
| +      (pixels = g_sw_draw_functions->access_pixels(env, java_canvas)) == NULL) {
 | 
| +    ScopedJavaLocalRef<jobject> jbitmap(java_helper_->CreateBitmap(
 | 
| +        env, clip.width(), clip.height()));
 | 
| +    if (!jbitmap.obj())
 | 
| +      return false;
 | 
| +
 | 
| +    if (!RasterizeIntoBitmap(env, jbitmap, clip.x(), clip.y(),
 | 
| +                             base::Bind(&BrowserViewRendererImpl::RenderSW,
 | 
| +                                        base::Unretained(this)))) {
 | 
| +      return false;
 | 
| +    }
 | 
| +
 | 
| +    ScopedJavaLocalRef<jobject> jcanvas(env, java_canvas);
 | 
| +    java_helper_->DrawBitmapIntoCanvas(env, jbitmap, jcanvas);
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  // Draw in a SkCanvas built over the pixel information.
 | 
| +  bool succeeded = false;
 | 
| +  {
 | 
| +    SkBitmap bitmap;
 | 
| +    bitmap.setConfig(static_cast<SkBitmap::Config>(pixels->config),
 | 
| +                     pixels->width,
 | 
| +                     pixels->height,
 | 
| +                     pixels->row_bytes);
 | 
| +    bitmap.setPixels(pixels->pixels);
 | 
| +    SkDevice device(bitmap);
 | 
| +    SkCanvas canvas(&device);
 | 
| +    SkMatrix matrix;
 | 
| +    for (int i = 0; i < 9; i++)
 | 
| +      matrix.set(i, pixels->matrix[i]);
 | 
| +    canvas.setMatrix(matrix);
 | 
| +
 | 
| +    SkRegion clip;
 | 
| +    if (pixels->clip_region_size) {
 | 
| +      size_t bytes_read = clip.readFromMemory(pixels->clip_region);
 | 
| +      DCHECK_EQ(pixels->clip_region_size, bytes_read);
 | 
| +      canvas.setClipRegion(clip);
 | 
| +    } else {
 | 
| +      clip.setRect(SkIRect::MakeWH(pixels->width, pixels->height));
 | 
| +    }
 | 
| +
 | 
| +    succeeded = RenderSW(&canvas);
 | 
| +  }
 | 
| +
 | 
| +  g_sw_draw_functions->release_pixels(pixels);
 | 
| +  return succeeded;
 | 
| +}
 | 
| +
 | 
| +ScopedJavaLocalRef<jobject> BrowserViewRendererImpl::CapturePicture() {
 | 
| +  skia::RefPtr<SkPicture> picture = GetLastCapturedPicture();
 | 
| +  if (!picture || !g_sw_draw_functions)
 | 
| +    return ScopedJavaLocalRef<jobject>();
 | 
| +
 | 
| +  JNIEnv* env = AttachCurrentThread();
 | 
| +  if (g_is_skia_version_compatible) {
 | 
| +    return ScopedJavaLocalRef<jobject>(env,
 | 
| +        g_sw_draw_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_helper_->CreateBitmap(
 | 
| +      env, picture->width(), picture->height()));
 | 
| +  if (!jbitmap.obj())
 | 
| +    return ScopedJavaLocalRef<jobject>();
 | 
| +
 | 
| +  if (!RasterizeIntoBitmap(env, jbitmap, 0, 0,
 | 
| +      base::Bind(&BrowserViewRendererImpl::RenderPicture,
 | 
| +                 base::Unretained(this)))) {
 | 
| +    return ScopedJavaLocalRef<jobject>();
 | 
| +  }
 | 
| +
 | 
| +  return java_helper_->RecordBitmapIntoPicture(env, jbitmap);
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::EnableOnNewPicture(OnNewPictureMode mode) {
 | 
| +  on_new_picture_mode_ = mode;
 | 
| +
 | 
| +  // 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);
 | 
| +  //DCHECK(view_renderer_host_);
 | 
| +  //view_renderer_host_->EnableCapturePictureCallback(
 | 
| +  //    on_new_picture_mode_ == kOnNewPictureEnabled);
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::OnVisibilityChanged(bool view_visible,
 | 
| +                                                  bool window_visible) {
 | 
| +  view_visible_ = window_visible && view_visible;
 | 
| +  Invalidate();
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::OnSizeChanged(int width, int height) {
 | 
| +  view_size_ = gfx::Size(width, height);
 | 
| +  view_clip_layer_->setBounds(view_size_);
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::OnAttachedToWindow(int width, int height) {
 | 
| +  view_size_ = gfx::Size(width, height);
 | 
| +  view_clip_layer_->setBounds(view_size_);
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::OnDetachedFromWindow() {
 | 
| +  view_visible_ = false;
 | 
| +  SetCompositorVisibility(false);
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::ScheduleComposite() {
 | 
| +  TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::ScheduleComposite");
 | 
| +
 | 
| +  if (is_composite_pending_)
 | 
| +    return;
 | 
| +
 | 
| +  is_composite_pending_ = true;
 | 
| +  Invalidate();
 | 
| +}
 | 
| +
 | 
| +skia::RefPtr<SkPicture> BrowserViewRendererImpl::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) {
 | 
| +    DCHECK(view_renderer_host_);
 | 
| +    view_renderer_host_->CapturePictureSync();
 | 
| +    picture = RendererPictureMap::GetInstance()->GetRendererPicture(
 | 
| +        web_contents_->GetRoutingID());
 | 
| +  }
 | 
| +
 | 
| +  return picture;
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::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)
 | 
| +    client_->OnNewPicture(CapturePicture());
 | 
| +
 | 
| +  // TODO(leandrogracia): delete when sw rendering uses Ubercompositor.
 | 
| +  // Invalidation should be provided by the compositor only.
 | 
| +  Invalidate();
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::SetCompositorVisibility(bool visible) {
 | 
| +  if (compositor_visible_ != visible) {
 | 
| +    compositor_visible_ = visible;
 | 
| +    compositor_->SetVisible(compositor_visible_);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::ResetCompositor() {
 | 
| +  compositor_.reset(content::Compositor::Create(this));
 | 
| +  compositor_->SetRootLayer(scissor_clip_layer_);
 | 
| +}
 | 
| +
 | 
| +void BrowserViewRendererImpl::Invalidate() {
 | 
| +  if (view_visible_)
 | 
| +    client_->Invalidate();
 | 
| +
 | 
| +  // When not in invalidation-only mode onNewPicture will be triggered
 | 
| +  // from the OnPictureUpdated callback.
 | 
| +  if (on_new_picture_mode_ == kOnNewPictureInvalidationOnly)
 | 
| +    client_->OnNewPicture(ScopedJavaLocalRef<jobject>());
 | 
| +}
 | 
| +
 | 
| +bool BrowserViewRendererImpl::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 BrowserViewRendererImpl::RenderPicture(SkCanvas* canvas) {
 | 
| +  skia::RefPtr<SkPicture> picture = GetLastCapturedPicture();
 | 
| +  if (!picture)
 | 
| +    return false;
 | 
| +
 | 
| +  // Correct device scale.
 | 
| +  canvas->scale(dpi_scale_, dpi_scale_);
 | 
| +
 | 
| +  picture->draw(canvas);
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +}  // namespace android_webview
 | 
| 
 |