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
{
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
,
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.";
65 if (AndroidBitmap_lockPixels(env
, jbitmap
.obj(), &pixels
) < 0) {
66 LOG(ERROR
) << "Error locking java bitmap pixels.";
73 bitmap
.setConfig(SkBitmap::kARGB_8888_Config
,
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.";
93 const void* kUserDataKey
= &kUserDataKey
;
97 class BrowserViewRendererImpl::UserData
: public content::WebContents::Data
{
99 UserData(BrowserViewRendererImpl
* ptr
) : instance_(ptr
) {}
100 virtual ~UserData() {
101 instance_
->WebContentsGone();
104 static BrowserViewRendererImpl
* GetInstance(content::WebContents
* contents
) {
107 UserData
* data
= reinterpret_cast<UserData
*>(
108 contents
->GetUserData(kUserDataKey
));
109 return data
? data
->instance_
: NULL
;
113 BrowserViewRendererImpl
* instance_
;
117 BrowserViewRendererImpl
* BrowserViewRendererImpl::Create(
118 BrowserViewRenderer::Client
* client
,
119 JavaHelper
* java_helper
) {
120 return new BrowserViewRendererImpl(client
, java_helper
);
124 BrowserViewRendererImpl
* BrowserViewRendererImpl::FromWebContents(
125 content::WebContents
* contents
) {
126 return UserData::GetInstance(contents
);
129 BrowserViewRendererImpl::BrowserViewRendererImpl(
130 BrowserViewRenderer::Client
* client
,
131 JavaHelper
* java_helper
)
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),
145 on_new_picture_mode_(kOnNewPictureDisabled
),
146 last_frame_context_(NULL
),
148 update_frame_info_callback_(
149 base::Bind(&BrowserViewRendererImpl::OnFrameInfoUpdated
,
150 base::Unretained(ALLOW_THIS_IN_INITIALIZER_LIST(this)))) {
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() {
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.
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
)
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());
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
)
215 DCHECK_EQ(draw_info
->mode
, AwDrawGLInfo::kModeDraw
);
217 SetCompositorVisibility(view_visible_
);
218 if (!compositor_visible_
)
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.";
229 if (last_frame_context_
!= current_context
) {
230 if (last_frame_context_
)
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());
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");
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()));
313 if (!RasterizeIntoBitmap(env
, jbitmap
, clip
.x(), clip
.y(),
314 base::Bind(&BrowserViewRendererImpl::RenderSW
,
315 base::Unretained(this)))) {
319 ScopedJavaLocalRef
<jobject
> jcanvas(env
, java_canvas
);
320 java_helper_
->DrawBitmapIntoCanvas(env
, jbitmap
, jcanvas
);
324 // Draw in a SkCanvas built over the pixel information.
325 bool succeeded
= false;
328 bitmap
.setConfig(static_cast<SkBitmap::Config
>(pixels
->config
),
332 bitmap
.setPixels(pixels
->pixels
);
333 SkDevice
device(bitmap
);
334 SkCanvas
canvas(&device
);
336 for (int i
= 0; i
< 9; i
++)
337 matrix
.set(i
, pixels
->matrix
[i
]);
338 canvas
.setMatrix(matrix
);
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
);
346 clip
.setRect(SkIRect::MakeWH(pixels
->width
, pixels
->height
));
349 succeeded
= RenderSW(&canvas
);
352 g_sw_draw_functions
->release_pixels(pixels
);
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()));
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
;
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_
)
442 is_composite_pending_
= true;
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.
455 view_renderer_host_
->CapturePictureSync();
456 picture
= RendererPictureMap::GetInstance()->GetRendererPicture(
457 web_contents_
->GetRoutingID());
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())
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.
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() {
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();
524 picture
->draw(canvas
);
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