Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / android_webview / browser / browser_view_renderer_impl.cc
blob83fc87d8578d685df47b3dae52e7cac5e145016a
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "android_webview/browser/browser_view_renderer_impl.h"
7 #include <android/bitmap.h>
9 #include "android_webview/common/renderer_picture_map.h"
10 #include "android_webview/public/browser/draw_gl.h"
11 #include "android_webview/public/browser/draw_sw.h"
12 #include "base/android/jni_android.h"
13 #include "base/debug/trace_event.h"
14 #include "base/logging.h"
15 #include "cc/layers/layer.h"
16 #include "content/public/browser/android/content_view_core.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
20 #include "third_party/skia/include/core/SkCanvas.h"
21 #include "third_party/skia/include/core/SkDevice.h"
22 #include "third_party/skia/include/core/SkGraphics.h"
23 #include "ui/gfx/size_conversions.h"
24 #include "ui/gfx/transform.h"
25 #include "ui/gfx/vector2d_f.h"
26 #include "ui/gl/gl_bindings.h"
28 using base::android::AttachCurrentThread;
29 using base::android::JavaRef;
30 using base::android::ScopedJavaLocalRef;
31 using content::Compositor;
32 using content::ContentViewCore;
34 namespace android_webview {
36 namespace {
38 // Provides software rendering functions from the Android glue layer.
39 // Allows preventing extra copies of data when rendering.
40 AwDrawSWFunctionTable* g_sw_draw_functions = NULL;
42 // Tells if the Skia library versions in Android and Chromium are compatible.
43 // If they are then it's possible to pass Skia objects like SkPictures to the
44 // Android glue layer via the SW rendering functions.
45 // If they are not, then additional copies and rasterizations are required
46 // as a fallback mechanism, which will have an important performance impact.
47 bool g_is_skia_version_compatible = false;
49 typedef base::Callback<bool(SkCanvas*)> RenderMethod;
51 static bool RasterizeIntoBitmap(JNIEnv* env,
52 const JavaRef<jobject>& jbitmap,
53 int scroll_x,
54 int scroll_y,
55 const RenderMethod& renderer) {
56 DCHECK(jbitmap.obj());
58 AndroidBitmapInfo bitmap_info;
59 if (AndroidBitmap_getInfo(env, jbitmap.obj(), &bitmap_info) < 0) {
60 LOG(ERROR) << "Error getting java bitmap info.";
61 return false;
64 void* pixels = NULL;
65 if (AndroidBitmap_lockPixels(env, jbitmap.obj(), &pixels) < 0) {
66 LOG(ERROR) << "Error locking java bitmap pixels.";
67 return false;
70 bool succeeded;
72 SkBitmap bitmap;
73 bitmap.setConfig(SkBitmap::kARGB_8888_Config,
74 bitmap_info.width,
75 bitmap_info.height,
76 bitmap_info.stride);
77 bitmap.setPixels(pixels);
79 SkDevice device(bitmap);
80 SkCanvas canvas(&device);
81 canvas.translate(-scroll_x, -scroll_y);
82 succeeded = renderer.Run(&canvas);
85 if (AndroidBitmap_unlockPixels(env, jbitmap.obj()) < 0) {
86 LOG(ERROR) << "Error unlocking java bitmap pixels.";
87 return false;
90 return succeeded;
93 const void* kUserDataKey = &kUserDataKey;
95 } // namespace
97 class BrowserViewRendererImpl::UserData : public content::WebContents::Data {
98 public:
99 UserData(BrowserViewRendererImpl* ptr) : instance_(ptr) {}
100 virtual ~UserData() {
101 instance_->WebContentsGone();
104 static BrowserViewRendererImpl* GetInstance(content::WebContents* contents) {
105 if (!contents)
106 return NULL;
107 UserData* data = reinterpret_cast<UserData*>(
108 contents->GetUserData(kUserDataKey));
109 return data ? data->instance_ : NULL;
112 private:
113 BrowserViewRendererImpl* instance_;
116 // static
117 BrowserViewRendererImpl* BrowserViewRendererImpl::Create(
118 BrowserViewRenderer::Client* client,
119 JavaHelper* java_helper) {
120 return new BrowserViewRendererImpl(client, java_helper);
123 // static
124 BrowserViewRendererImpl* BrowserViewRendererImpl::FromWebContents(
125 content::WebContents* contents) {
126 return UserData::GetInstance(contents);
129 BrowserViewRendererImpl::BrowserViewRendererImpl(
130 BrowserViewRenderer::Client* client,
131 JavaHelper* java_helper)
132 : client_(client),
133 java_helper_(java_helper),
134 view_renderer_host_(new ViewRendererHost(NULL, this)),
135 ALLOW_THIS_IN_INITIALIZER_LIST(compositor_(Compositor::Create(this))),
136 view_clip_layer_(cc::Layer::Create()),
137 transform_layer_(cc::Layer::Create()),
138 scissor_clip_layer_(cc::Layer::Create()),
139 view_attached_(false),
140 view_visible_(false),
141 compositor_visible_(false),
142 is_composite_pending_(false),
143 dpi_scale_(1.0f),
144 page_scale_(1.0f),
145 on_new_picture_mode_(kOnNewPictureDisabled),
146 last_frame_context_(NULL),
147 web_contents_(NULL),
148 update_frame_info_callback_(
149 base::Bind(&BrowserViewRendererImpl::OnFrameInfoUpdated,
150 base::Unretained(ALLOW_THIS_IN_INITIALIZER_LIST(this)))) {
152 DCHECK(java_helper);
154 // Define the view hierarchy.
155 transform_layer_->AddChild(view_clip_layer_);
156 scissor_clip_layer_->AddChild(transform_layer_);
157 compositor_->SetRootLayer(scissor_clip_layer_);
159 RendererPictureMap::CreateInstance();
162 BrowserViewRendererImpl::~BrowserViewRendererImpl() {
163 SetContents(NULL);
166 // static
167 void BrowserViewRendererImpl::SetAwDrawSWFunctionTable(
168 AwDrawSWFunctionTable* table) {
169 g_sw_draw_functions = table;
170 g_is_skia_version_compatible =
171 g_sw_draw_functions->is_skia_version_compatible(&SkGraphics::GetVersion);
172 LOG_IF(WARNING, !g_is_skia_version_compatible)
173 << "Skia versions are not compatible, rendering performance will suffer.";
176 void BrowserViewRendererImpl::SetContents(ContentViewCore* content_view_core) {
177 // First remove association from the prior ContentViewCore / WebContents.
178 if (web_contents_) {
179 ContentViewCore* previous_content_view_core =
180 ContentViewCore::FromWebContents(web_contents_);
181 if (previous_content_view_core) {
182 previous_content_view_core->RemoveFrameInfoCallback(
183 update_frame_info_callback_);
185 web_contents_->SetUserData(kUserDataKey, NULL);
186 DCHECK(!web_contents_); // WebContentsGone should have been called.
189 if (!content_view_core)
190 return;
192 web_contents_ = content_view_core->GetWebContents();
193 web_contents_->SetUserData(kUserDataKey, new UserData(this));
194 view_renderer_host_->Observe(web_contents_);
196 content_view_core->AddFrameInfoCallback(update_frame_info_callback_);
197 dpi_scale_ = content_view_core->GetDpiScale();
199 view_clip_layer_->RemoveAllChildren();
200 view_clip_layer_->AddChild(content_view_core->GetLayer());
201 Invalidate();
204 void BrowserViewRendererImpl::WebContentsGone() {
205 web_contents_ = NULL;
208 void BrowserViewRendererImpl::DrawGL(AwDrawGLInfo* draw_info) {
209 TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::DrawGL");
211 if (view_size_.IsEmpty() || !scissor_clip_layer_ ||
212 draw_info->mode == AwDrawGLInfo::kModeProcess)
213 return;
215 DCHECK_EQ(draw_info->mode, AwDrawGLInfo::kModeDraw);
217 SetCompositorVisibility(view_visible_);
218 if (!compositor_visible_)
219 return;
221 // We need to watch if the current Android context has changed and enforce
222 // a clean-up in the compositor.
223 EGLContext current_context = eglGetCurrentContext();
224 if (!current_context) {
225 LOG(WARNING) << "No current context attached. Skipping composite.";
226 return;
229 if (last_frame_context_ != current_context) {
230 if (last_frame_context_)
231 ResetCompositor();
232 last_frame_context_ = current_context;
235 compositor_->SetWindowBounds(gfx::Size(draw_info->width, draw_info->height));
237 if (draw_info->is_layer) {
238 // When rendering into a separate layer no view clipping, transform,
239 // scissoring or background transparency need to be handled.
240 // The Android framework will composite us afterwards.
241 compositor_->SetHasTransparentBackground(false);
242 view_clip_layer_->SetMasksToBounds(false);
243 transform_layer_->SetTransform(gfx::Transform());
244 scissor_clip_layer_->SetMasksToBounds(false);
245 scissor_clip_layer_->SetPosition(gfx::PointF());
246 scissor_clip_layer_->SetBounds(gfx::Size());
247 scissor_clip_layer_->SetSublayerTransform(gfx::Transform());
249 } else {
250 compositor_->SetHasTransparentBackground(true);
252 gfx::Rect clip_rect(draw_info->clip_left, draw_info->clip_top,
253 draw_info->clip_right - draw_info->clip_left,
254 draw_info->clip_bottom - draw_info->clip_top);
256 scissor_clip_layer_->SetPosition(clip_rect.origin());
257 scissor_clip_layer_->SetBounds(clip_rect.size());
258 scissor_clip_layer_->SetMasksToBounds(true);
260 // The compositor clipping architecture enforces us to have the clip layer
261 // as an ancestor of the area we want to clip, but this makes the transform
262 // become relative to the clip area rather than the full surface. The clip
263 // position offset needs to be undone before applying the transform.
264 gfx::Transform undo_clip_position;
265 undo_clip_position.Translate(-clip_rect.x(), -clip_rect.y());
266 scissor_clip_layer_->SetSublayerTransform(undo_clip_position);
268 gfx::Transform transform;
269 transform.matrix().setColMajorf(draw_info->transform);
271 // The scrolling values of the Android Framework affect the transformation
272 // matrix. This needs to be undone to let the compositor handle scrolling.
273 // TODO(leandrogracia): when scrolling becomes synchronous we should undo
274 // or override the translation in the compositor, not the one coming from
275 // the Android View System, as it could be out of bounds for overscrolling.
276 transform.Translate(hw_rendering_scroll_.x(), hw_rendering_scroll_.y());
277 transform_layer_->SetTransform(transform);
279 view_clip_layer_->SetMasksToBounds(true);
282 compositor_->Composite();
283 is_composite_pending_ = false;
285 // The GL functor must ensure these are set to zero before returning.
286 // Not setting them leads to graphical artifacts that can affect other apps.
287 glBindBuffer(GL_ARRAY_BUFFER, 0);
288 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
291 void BrowserViewRendererImpl::SetScrollForHWFrame(int x, int y) {
292 hw_rendering_scroll_ = gfx::Point(x, y);
295 bool BrowserViewRendererImpl::DrawSW(jobject java_canvas,
296 const gfx::Rect& clip) {
297 TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::DrawSW");
299 if (clip.IsEmpty())
300 return true;
302 AwPixelInfo* pixels;
303 JNIEnv* env = AttachCurrentThread();
305 // Render into an auxiliary bitmap if pixel info is not available.
306 if (!g_sw_draw_functions ||
307 (pixels = g_sw_draw_functions->access_pixels(env, java_canvas)) == NULL) {
308 ScopedJavaLocalRef<jobject> jbitmap(java_helper_->CreateBitmap(
309 env, clip.width(), clip.height()));
310 if (!jbitmap.obj())
311 return false;
313 if (!RasterizeIntoBitmap(env, jbitmap, clip.x(), clip.y(),
314 base::Bind(&BrowserViewRendererImpl::RenderSW,
315 base::Unretained(this)))) {
316 return false;
319 ScopedJavaLocalRef<jobject> jcanvas(env, java_canvas);
320 java_helper_->DrawBitmapIntoCanvas(env, jbitmap, jcanvas);
321 return true;
324 // Draw in a SkCanvas built over the pixel information.
325 bool succeeded = false;
327 SkBitmap bitmap;
328 bitmap.setConfig(static_cast<SkBitmap::Config>(pixels->config),
329 pixels->width,
330 pixels->height,
331 pixels->row_bytes);
332 bitmap.setPixels(pixels->pixels);
333 SkDevice device(bitmap);
334 SkCanvas canvas(&device);
335 SkMatrix matrix;
336 for (int i = 0; i < 9; i++)
337 matrix.set(i, pixels->matrix[i]);
338 canvas.setMatrix(matrix);
340 SkRegion clip;
341 if (pixels->clip_region_size) {
342 size_t bytes_read = clip.readFromMemory(pixels->clip_region);
343 DCHECK_EQ(pixels->clip_region_size, bytes_read);
344 canvas.setClipRegion(clip);
345 } else {
346 clip.setRect(SkIRect::MakeWH(pixels->width, pixels->height));
349 succeeded = RenderSW(&canvas);
352 g_sw_draw_functions->release_pixels(pixels);
353 return succeeded;
356 ScopedJavaLocalRef<jobject> BrowserViewRendererImpl::CapturePicture() {
357 skia::RefPtr<SkPicture> picture = GetLastCapturedPicture();
358 if (!picture || !g_sw_draw_functions)
359 return ScopedJavaLocalRef<jobject>();
361 // Return empty Picture objects for empty SkPictures.
362 JNIEnv* env = AttachCurrentThread();
363 if (picture->width() <= 0 || picture->height() <= 0) {
364 return java_helper_->RecordBitmapIntoPicture(
365 env, ScopedJavaLocalRef<jobject>());
368 if (g_is_skia_version_compatible) {
369 return ScopedJavaLocalRef<jobject>(env,
370 g_sw_draw_functions->create_picture(env, picture->clone()));
373 // If Skia versions are not compatible, workaround it by rasterizing the
374 // picture into a bitmap and drawing it into a new Java picture.
375 ScopedJavaLocalRef<jobject> jbitmap(java_helper_->CreateBitmap(
376 env, picture->width(), picture->height()));
377 if (!jbitmap.obj())
378 return ScopedJavaLocalRef<jobject>();
380 if (!RasterizeIntoBitmap(env, jbitmap, 0, 0,
381 base::Bind(&BrowserViewRendererImpl::RenderPicture,
382 base::Unretained(this)))) {
383 return ScopedJavaLocalRef<jobject>();
386 return java_helper_->RecordBitmapIntoPicture(env, jbitmap);
389 void BrowserViewRendererImpl::EnableOnNewPicture(OnNewPictureMode mode) {
390 on_new_picture_mode_ = mode;
392 // TODO(leandrogracia): when SW rendering uses the compositor rather than
393 // picture rasterization, send update the renderer side with the correct
394 // listener state. (For now, we always leave render picture listener enabled).
395 // render_view_host_ext_->EnableCapturePictureCallback(enabled);
396 //DCHECK(view_renderer_host_);
397 //view_renderer_host_->EnableCapturePictureCallback(
398 // on_new_picture_mode_ == kOnNewPictureEnabled);
401 void BrowserViewRendererImpl::OnVisibilityChanged(bool view_visible,
402 bool window_visible) {
403 view_visible_ = window_visible && view_visible;
404 Invalidate();
407 void BrowserViewRendererImpl::OnSizeChanged(int width, int height) {
408 view_size_ = gfx::Size(width, height);
409 view_clip_layer_->SetBounds(view_size_);
412 void BrowserViewRendererImpl::OnAttachedToWindow(int width, int height) {
413 view_attached_ = true;
414 view_size_ = gfx::Size(width, height);
415 view_clip_layer_->SetBounds(view_size_);
418 void BrowserViewRendererImpl::OnDetachedFromWindow() {
419 view_attached_ = false;
420 view_visible_ = false;
421 SetCompositorVisibility(false);
424 bool BrowserViewRendererImpl::IsAttachedToWindow() {
425 return view_attached_;
428 bool BrowserViewRendererImpl::IsViewVisible() {
429 return view_visible_;
432 gfx::Rect BrowserViewRendererImpl::GetScreenRect() {
433 return gfx::Rect(client_->GetLocationOnScreen(), view_size_);
436 void BrowserViewRendererImpl::ScheduleComposite() {
437 TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::ScheduleComposite");
439 if (is_composite_pending_)
440 return;
442 is_composite_pending_ = true;
443 Invalidate();
446 skia::RefPtr<SkPicture> BrowserViewRendererImpl::GetLastCapturedPicture() {
447 // Use the latest available picture if the listener callback is enabled.
448 skia::RefPtr<SkPicture> picture;
449 if (on_new_picture_mode_ == kOnNewPictureEnabled)
450 picture = RendererPictureMap::GetInstance()->GetRendererPicture(
451 web_contents_->GetRoutingID());
453 // If not available or not in listener mode get it synchronously.
454 if (!picture) {
455 view_renderer_host_->CapturePictureSync();
456 picture = RendererPictureMap::GetInstance()->GetRendererPicture(
457 web_contents_->GetRoutingID());
460 return picture;
463 void BrowserViewRendererImpl::OnPictureUpdated(int process_id,
464 int render_view_id) {
465 CHECK_EQ(web_contents_->GetRenderProcessHost()->GetID(), process_id);
466 if (render_view_id != web_contents_->GetRoutingID())
467 return;
469 // TODO(leandrogracia): this can be made unconditional once software rendering
470 // uses Ubercompositor. Until then this path is required for SW invalidations.
471 if (on_new_picture_mode_ == kOnNewPictureEnabled)
472 client_->OnNewPicture(CapturePicture());
474 // TODO(leandrogracia): delete when sw rendering uses Ubercompositor.
475 // Invalidation should be provided by the compositor only.
476 Invalidate();
479 void BrowserViewRendererImpl::SetCompositorVisibility(bool visible) {
480 if (compositor_visible_ != visible) {
481 compositor_visible_ = visible;
482 compositor_->SetVisible(compositor_visible_);
486 void BrowserViewRendererImpl::ResetCompositor() {
487 compositor_.reset(content::Compositor::Create(this));
488 compositor_->SetRootLayer(scissor_clip_layer_);
491 void BrowserViewRendererImpl::Invalidate() {
492 if (view_visible_)
493 client_->Invalidate();
495 // When not in invalidation-only mode onNewPicture will be triggered
496 // from the OnPictureUpdated callback.
497 if (on_new_picture_mode_ == kOnNewPictureInvalidationOnly)
498 client_->OnNewPicture(ScopedJavaLocalRef<jobject>());
501 bool BrowserViewRendererImpl::RenderSW(SkCanvas* canvas) {
502 float content_scale = dpi_scale_ * page_scale_;
503 canvas->scale(content_scale, content_scale);
505 // Clear to white any parts of the view not covered by the scaled contents.
506 // TODO(leandrogracia): this should be automatically done by the SW rendering
507 // path once multiple layers are supported.
508 gfx::Size physical_content_size = gfx::ToCeiledSize(
509 gfx::ScaleSize(content_size_css_, content_scale));
510 if (physical_content_size.width() < view_size_.width() ||
511 physical_content_size.height() < view_size_.height())
512 canvas->clear(SK_ColorWHITE);
514 // TODO(leandrogracia): use the appropriate SW rendering path when available
515 // instead of abusing CapturePicture.
516 return RenderPicture(canvas);
519 bool BrowserViewRendererImpl::RenderPicture(SkCanvas* canvas) {
520 skia::RefPtr<SkPicture> picture = GetLastCapturedPicture();
521 if (!picture)
522 return false;
524 picture->draw(canvas);
525 return true;
528 void BrowserViewRendererImpl::OnFrameInfoUpdated(
529 const gfx::SizeF& content_size,
530 const gfx::Vector2dF& scroll_offset,
531 float page_scale_factor) {
532 page_scale_ = page_scale_factor;
533 content_size_css_ = content_size;
536 } // namespace android_webview