Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(433)

Side by Side Diff: android_webview/browser/browser_view_renderer_impl.cc

Issue 12041009: [Android WebView] Migrate the rendering code to a separate set of classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: upload error, re-uploading. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "android_webview/browser/browser_view_renderer_impl.h"
6
7 #include <android/bitmap.h>
8 #include <sys/system_properties.h>
9
10 #include "android_webview/common/renderer_picture_map.h"
11 #include "android_webview/public/browser/draw_gl.h"
12 #include "android_webview/public/browser/draw_sw.h"
13 #include "base/android/jni_android.h"
14 #include "base/debug/trace_event.h"
15 #include "base/logging.h"
16 #include "cc/layer.h"
17 #include "content/public/browser/android/content_view_core.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "third_party/skia/include/core/SkCanvas.h"
22 #include "third_party/skia/include/core/SkDevice.h"
23 #include "third_party/skia/include/core/SkGraphics.h"
24 #include "ui/gfx/size.h"
25 #include "ui/gfx/transform.h"
26 #include "ui/gl/gl_bindings.h"
27
28 // TODO(leandrogracia): remove when crbug.com/164140 is closed.
29 // Borrowed from gl2ext.h. Cannot be included due to conflicts with
30 // gl_bindings.h and the EGL library methods (eglGetCurrentContext).
31 #ifndef GL_TEXTURE_EXTERNAL_OES
32 #define GL_TEXTURE_EXTERNAL_OES 0x8D65
33 #endif
34
35 #ifndef GL_TEXTURE_BINDING_EXTERNAL_OES
36 #define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67
37 #endif
38
39 using base::android::AttachCurrentThread;
40 using base::android::JavaRef;
41 using base::android::ScopedJavaLocalRef;
42 using content::Compositor;
43 using content::ContentViewCore;
44
45 namespace {
46
47 // Provides software rendering functions from the Android glue layer.
48 // Allows preventing extra copies of data when rendering.
49 AwDrawSWFunctionTable* g_sw_draw_functions = NULL;
50
51 // Tells if the Skia library versions in Android and Chromium are compatible.
52 // If they are then it's possible to pass Skia objects like SkPictures to the
53 // Android glue layer via the SW rendering functions.
54 // If they are not, then additional copies and rasterizations are required
55 // as a fallback mechanism, which will have an important performance impact.
56 bool g_is_skia_version_compatible = false;
57
58 typedef base::Callback<bool(SkCanvas*)> RenderMethod;
59
60 static bool RasterizeIntoBitmap(JNIEnv* env,
61 const JavaRef<jobject>& jbitmap,
62 int scroll_x,
63 int scroll_y,
64 const RenderMethod& renderer) {
65 DCHECK(jbitmap.obj());
66
67 AndroidBitmapInfo bitmap_info;
68 if (AndroidBitmap_getInfo(env, jbitmap.obj(), &bitmap_info) < 0) {
69 LOG(ERROR) << "Error getting java bitmap info.";
70 return false;
71 }
72
73 void* pixels = NULL;
74 if (AndroidBitmap_lockPixels(env, jbitmap.obj(), &pixels) < 0) {
75 LOG(ERROR) << "Error locking java bitmap pixels.";
76 return false;
77 }
78
79 bool succeeded;
80 {
81 SkBitmap bitmap;
82 bitmap.setConfig(SkBitmap::kARGB_8888_Config,
83 bitmap_info.width,
84 bitmap_info.height,
85 bitmap_info.stride);
86 bitmap.setPixels(pixels);
87
88 SkDevice device(bitmap);
89 SkCanvas canvas(&device);
90 canvas.translate(-scroll_x, -scroll_y);
91 succeeded = renderer.Run(&canvas);
92 }
93
94 if (AndroidBitmap_unlockPixels(env, jbitmap.obj()) < 0) {
95 LOG(ERROR) << "Error unlocking java bitmap pixels.";
96 return false;
97 }
98
99 return succeeded;
100 }
101
102 } // namespace
103
104 namespace android_webview {
105
106 // static
107 BrowserViewRendererImpl* BrowserViewRendererImpl::Create(
108 BrowserViewRenderer::Client* client,
109 JavaHelper* java_helper) {
110 return new BrowserViewRendererImpl(client, java_helper);
111 }
112
113 BrowserViewRendererImpl::BrowserViewRendererImpl(
114 BrowserViewRenderer::Client* client,
115 JavaHelper* java_helper)
116 : client_(client),
117 java_helper_(java_helper),
118 ALLOW_THIS_IN_INITIALIZER_LIST(compositor_(Compositor::Create(this))),
119 view_clip_layer_(cc::Layer::create()),
120 transform_layer_(cc::Layer::create()),
121 scissor_clip_layer_(cc::Layer::create()),
122 view_visible_(false),
123 compositor_visible_(false),
124 is_composite_pending_(false),
125 dpi_scale_(1.0f),
126 on_new_picture_mode_(kOnNewPictureDisabled),
127 last_frame_context_(NULL),
128 web_contents_(NULL) {
129 DCHECK(java_helper);
130
131 // Define the view hierarchy.
132 transform_layer_->addChild(view_clip_layer_);
133 scissor_clip_layer_->addChild(transform_layer_);
134 compositor_->SetRootLayer(scissor_clip_layer_);
135
136 RendererPictureMap::CreateInstance();
137 }
138
139 BrowserViewRendererImpl::~BrowserViewRendererImpl() {
140 }
141
142 // static
143 void BrowserViewRendererImpl::SetAwDrawSWFunctionTable(
144 AwDrawSWFunctionTable* table) {
145 g_sw_draw_functions = table;
146 g_is_skia_version_compatible =
147 g_sw_draw_functions->is_skia_version_compatible(&SkGraphics::GetVersion);
148 LOG_IF(WARNING, !g_is_skia_version_compatible)
149 << "Skia versions are not compatible, rendering performance will suffer.";
150 }
151
152 void BrowserViewRendererImpl::SetContents(ContentViewCore* content_view_core) {
153 dpi_scale_ = content_view_core->GetDpiScale();
154 web_contents_ = content_view_core->GetWebContents();
155 if (!view_renderer_host_)
156 view_renderer_host_.reset(new ViewRendererHost(web_contents_, this));
157 else
158 view_renderer_host_->Observe(web_contents_);
159
160 view_clip_layer_->removeAllChildren();
161 view_clip_layer_->addChild(content_view_core->GetLayer());
162 Invalidate();
163 }
164
165 void BrowserViewRendererImpl::DrawGL(AwDrawGLInfo* draw_info) {
166 TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::DrawGL");
167
168 if (view_size_.IsEmpty() || !scissor_clip_layer_ ||
169 draw_info->mode == AwDrawGLInfo::kModeProcess)
170 return;
171
172 DCHECK_EQ(draw_info->mode, AwDrawGLInfo::kModeDraw);
173
174 SetCompositorVisibility(view_visible_);
175 if (!compositor_visible_)
176 return;
177
178 // TODO(leandrogracia): remove when crbug.com/164140 is closed.
179 // ---------------------------------------------------------------------------
180 GLint texture_external_oes_binding;
181 glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_external_oes_binding);
182
183 GLint vertex_array_buffer_binding;
184 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vertex_array_buffer_binding);
185
186 GLint index_array_buffer_binding;
187 glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &index_array_buffer_binding);
188
189 GLint pack_alignment;
190 glGetIntegerv(GL_PACK_ALIGNMENT, &pack_alignment);
191
192 GLint unpack_alignment;
193 glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment);
194
195 struct {
196 GLint enabled;
197 GLint size;
198 GLint type;
199 GLint normalized;
200 GLint stride;
201 GLvoid* pointer;
202 } vertex_attrib[3];
203
204 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(vertex_attrib); ++i) {
205 glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED,
206 &vertex_attrib[i].enabled);
207 glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE,
208 &vertex_attrib[i].size);
209 glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE,
210 &vertex_attrib[i].type);
211 glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED,
212 &vertex_attrib[i].normalized);
213 glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE,
214 &vertex_attrib[i].stride);
215 glGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER,
216 &vertex_attrib[i].pointer);
217 }
218
219 GLboolean depth_test;
220 glGetBooleanv(GL_DEPTH_TEST, &depth_test);
221
222 GLboolean cull_face;
223 glGetBooleanv(GL_CULL_FACE, &cull_face);
224
225 GLboolean color_mask[4];
226 glGetBooleanv(GL_COLOR_WRITEMASK, color_mask);
227
228 GLboolean blend_enabled;
229 glGetBooleanv(GL_BLEND, &blend_enabled);
230
231 GLint blend_src_rgb;
232 glGetIntegerv(GL_BLEND_SRC_RGB, &blend_src_rgb);
233
234 GLint blend_src_alpha;
235 glGetIntegerv(GL_BLEND_SRC_ALPHA, &blend_src_alpha);
236
237 GLint blend_dest_rgb;
238 glGetIntegerv(GL_BLEND_DST_RGB, &blend_dest_rgb);
239
240 GLint blend_dest_alpha;
241 glGetIntegerv(GL_BLEND_DST_ALPHA, &blend_dest_alpha);
242
243 GLint active_texture;
244 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
245
246 GLint viewport[4];
247 glGetIntegerv(GL_VIEWPORT, viewport);
248
249 GLboolean scissor_test;
250 glGetBooleanv(GL_SCISSOR_TEST, &scissor_test);
251
252 GLint scissor_box[4];
253 glGetIntegerv(GL_SCISSOR_BOX, scissor_box);
254
255 GLint current_program;
256 glGetIntegerv(GL_CURRENT_PROGRAM, &current_program);
257 // ---------------------------------------------------------------------------
258
259 // We need to watch if the current Android context has changed and enforce
260 // a clean-up in the compositor.
261 EGLContext current_context = eglGetCurrentContext();
262 if (!current_context) {
263 LOG(WARNING) << "No current context attached. Skipping composite.";
264 return;
265 }
266
267 if (last_frame_context_ != current_context) {
268 if (last_frame_context_)
269 ResetCompositor();
270 last_frame_context_ = current_context;
271 }
272
273 compositor_->SetWindowBounds(gfx::Size(draw_info->width, draw_info->height));
274
275 if (draw_info->is_layer) {
276 // When rendering into a separate layer no view clipping, transform,
277 // scissoring or background transparency need to be handled.
278 // The Android framework will composite us afterwards.
279 compositor_->SetHasTransparentBackground(false);
280 view_clip_layer_->setMasksToBounds(false);
281 transform_layer_->setTransform(gfx::Transform());
282 scissor_clip_layer_->setMasksToBounds(false);
283 scissor_clip_layer_->setPosition(gfx::PointF());
284 scissor_clip_layer_->setBounds(gfx::Size());
285 scissor_clip_layer_->setSublayerTransform(gfx::Transform());
286
287 } else {
288 compositor_->SetHasTransparentBackground(true);
289
290 gfx::Rect clip_rect(draw_info->clip_left, draw_info->clip_top,
291 draw_info->clip_right - draw_info->clip_left,
292 draw_info->clip_bottom - draw_info->clip_top);
293
294 scissor_clip_layer_->setPosition(clip_rect.origin());
295 scissor_clip_layer_->setBounds(clip_rect.size());
296 scissor_clip_layer_->setMasksToBounds(true);
297
298 // The compositor clipping architecture enforces us to have the clip layer
299 // as an ancestor of the area we want to clip, but this makes the transform
300 // become relative to the clip area rather than the full surface. The clip
301 // position offset needs to be undone before applying the transform.
302 gfx::Transform undo_clip_position;
303 undo_clip_position.Translate(-clip_rect.x(), -clip_rect.y());
304 scissor_clip_layer_->setSublayerTransform(undo_clip_position);
305
306 gfx::Transform transform;
307 transform.matrix().setColMajorf(draw_info->transform);
308
309 // The scrolling values of the Android Framework affect the transformation
310 // matrix. This needs to be undone to let the compositor handle scrolling.
311 // TODO(leandrogracia): when scrolling becomes synchronous we should undo
312 // or override the translation in the compositor, not the one coming from
313 // the Android View System, as it could be out of bounds for overscrolling.
314 transform.Translate(hw_rendering_scroll_.x(), hw_rendering_scroll_.y());
315 transform_layer_->setTransform(transform);
316
317 view_clip_layer_->setMasksToBounds(true);
318 }
319
320 compositor_->Composite();
321 is_composite_pending_ = false;
322
323 // TODO(leandrogracia): remove when crbug.com/164140 is closed.
324 // ---------------------------------------------------------------------------
325 char no_gl_restore_prop[PROP_VALUE_MAX];
326 __system_property_get("webview.chromium_no_gl_restore", no_gl_restore_prop);
327 if (!strcmp(no_gl_restore_prop, "true")) {
328 LOG(WARNING) << "Android GL functor not restoring the previous GL state.";
329 } else {
330 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_external_oes_binding);
331 glBindBuffer(GL_ARRAY_BUFFER, vertex_array_buffer_binding);
332 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_buffer_binding);
333
334 glPixelStorei(GL_PACK_ALIGNMENT, pack_alignment);
335 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
336
337 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(vertex_attrib); ++i) {
338 glVertexAttribPointer(i, vertex_attrib[i].size,
339 vertex_attrib[i].type, vertex_attrib[i].normalized,
340 vertex_attrib[i].stride, vertex_attrib[i].pointer);
341
342 if (vertex_attrib[i].enabled)
343 glEnableVertexAttribArray(i);
344 else
345 glDisableVertexAttribArray(i);
346 }
347
348 if (depth_test)
349 glEnable(GL_DEPTH_TEST);
350 else
351 glDisable(GL_DEPTH_TEST);
352
353 if (cull_face)
354 glEnable(GL_CULL_FACE);
355 else
356 glDisable(GL_CULL_FACE);
357
358 glColorMask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
359
360 if (blend_enabled)
361 glEnable(GL_BLEND);
362 else
363 glDisable(GL_BLEND);
364
365 glBlendFuncSeparate(blend_src_rgb, blend_dest_rgb,
366 blend_src_alpha, blend_dest_alpha);
367
368 glActiveTexture(active_texture);
369
370 glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
371
372 if (scissor_test)
373 glEnable(GL_SCISSOR_TEST);
374 else
375 glDisable(GL_SCISSOR_TEST);
376
377 glScissor(scissor_box[0], scissor_box[1], scissor_box[2],
378 scissor_box[3]);
379
380 glUseProgram(current_program);
381 }
382 // ---------------------------------------------------------------------------
383 }
384
385 void BrowserViewRendererImpl::SetScrollForHWFrame(int x, int y) {
386 hw_rendering_scroll_ = gfx::Point(x, y);
387 }
388
389 bool BrowserViewRendererImpl::DrawSW(jobject java_canvas,
390 const gfx::Rect& clip) {
391 TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::DrawSW");
392
393 if (clip.IsEmpty())
394 return true;
395
396 AwPixelInfo* pixels;
397 JNIEnv* env = AttachCurrentThread();
398
399 // Render into an auxiliary bitmap if pixel info is not available.
400 if (!g_sw_draw_functions ||
401 (pixels = g_sw_draw_functions->access_pixels(env, java_canvas)) == NULL) {
402 ScopedJavaLocalRef<jobject> jbitmap(java_helper_->CreateBitmap(
403 env, clip.width(), clip.height()));
404 if (!jbitmap.obj())
405 return false;
406
407 if (!RasterizeIntoBitmap(env, jbitmap, clip.x(), clip.y(),
408 base::Bind(&BrowserViewRendererImpl::RenderSW,
409 base::Unretained(this)))) {
410 return false;
411 }
412
413 ScopedJavaLocalRef<jobject> jcanvas(env, java_canvas);
414 java_helper_->DrawBitmapIntoCanvas(env, jbitmap, jcanvas);
415 return true;
416 }
417
418 // Draw in a SkCanvas built over the pixel information.
419 bool succeeded = false;
420 {
421 SkBitmap bitmap;
422 bitmap.setConfig(static_cast<SkBitmap::Config>(pixels->config),
423 pixels->width,
424 pixels->height,
425 pixels->row_bytes);
426 bitmap.setPixels(pixels->pixels);
427 SkDevice device(bitmap);
428 SkCanvas canvas(&device);
429 SkMatrix matrix;
430 for (int i = 0; i < 9; i++)
431 matrix.set(i, pixels->matrix[i]);
432 canvas.setMatrix(matrix);
433
434 SkRegion clip;
435 if (pixels->clip_region_size) {
436 size_t bytes_read = clip.readFromMemory(pixels->clip_region);
437 DCHECK_EQ(pixels->clip_region_size, bytes_read);
438 canvas.setClipRegion(clip);
439 } else {
440 clip.setRect(SkIRect::MakeWH(pixels->width, pixels->height));
441 }
442
443 succeeded = RenderSW(&canvas);
444 }
445
446 g_sw_draw_functions->release_pixels(pixels);
447 return succeeded;
448 }
449
450 ScopedJavaLocalRef<jobject> BrowserViewRendererImpl::CapturePicture() {
451 skia::RefPtr<SkPicture> picture = GetLastCapturedPicture();
452 if (!picture || !g_sw_draw_functions)
453 return ScopedJavaLocalRef<jobject>();
454
455 JNIEnv* env = AttachCurrentThread();
456 if (g_is_skia_version_compatible) {
457 return ScopedJavaLocalRef<jobject>(env,
458 g_sw_draw_functions->create_picture(env, picture->clone()));
459 }
460
461 // If Skia versions are not compatible, workaround it by rasterizing the
462 // picture into a bitmap and drawing it into a new Java picture.
463 ScopedJavaLocalRef<jobject> jbitmap(java_helper_->CreateBitmap(
464 env, picture->width(), picture->height()));
465 if (!jbitmap.obj())
466 return ScopedJavaLocalRef<jobject>();
467
468 if (!RasterizeIntoBitmap(env, jbitmap, 0, 0,
469 base::Bind(&BrowserViewRendererImpl::RenderPicture,
470 base::Unretained(this)))) {
471 return ScopedJavaLocalRef<jobject>();
472 }
473
474 return java_helper_->RecordBitmapIntoPicture(env, jbitmap);
475 }
476
477 void BrowserViewRendererImpl::EnableOnNewPicture(OnNewPictureMode mode) {
478 on_new_picture_mode_ = mode;
479
480 // TODO(leandrogracia): when SW rendering uses the compositor rather than
481 // picture rasterization, send update the renderer side with the correct
482 // listener state. (For now, we always leave render picture listener enabled).
483 // render_view_host_ext_->EnableCapturePictureCallback(enabled);
484 //DCHECK(view_renderer_host_);
485 //view_renderer_host_->EnableCapturePictureCallback(
486 // on_new_picture_mode_ == kOnNewPictureEnabled);
487 }
488
489 void BrowserViewRendererImpl::OnVisibilityChanged(bool view_visible,
490 bool window_visible) {
491 view_visible_ = window_visible && view_visible;
492 Invalidate();
493 }
494
495 void BrowserViewRendererImpl::OnSizeChanged(int width, int height) {
496 view_size_ = gfx::Size(width, height);
497 view_clip_layer_->setBounds(view_size_);
498 }
499
500 void BrowserViewRendererImpl::OnAttachedToWindow(int width, int height) {
501 view_size_ = gfx::Size(width, height);
502 view_clip_layer_->setBounds(view_size_);
503 }
504
505 void BrowserViewRendererImpl::OnDetachedFromWindow() {
506 view_visible_ = false;
507 SetCompositorVisibility(false);
508 }
509
510 void BrowserViewRendererImpl::ScheduleComposite() {
511 TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::ScheduleComposite");
512
513 if (is_composite_pending_)
514 return;
515
516 is_composite_pending_ = true;
517 Invalidate();
518 }
519
520 skia::RefPtr<SkPicture> BrowserViewRendererImpl::GetLastCapturedPicture() {
521 // Use the latest available picture if the listener callback is enabled.
522 skia::RefPtr<SkPicture> picture;
523 if (on_new_picture_mode_ == kOnNewPictureEnabled)
524 picture = RendererPictureMap::GetInstance()->GetRendererPicture(
525 web_contents_->GetRoutingID());
526
527 // If not available or not in listener mode get it synchronously.
528 if (!picture) {
529 DCHECK(view_renderer_host_);
530 view_renderer_host_->CapturePictureSync();
531 picture = RendererPictureMap::GetInstance()->GetRendererPicture(
532 web_contents_->GetRoutingID());
533 }
534
535 return picture;
536 }
537
538 void BrowserViewRendererImpl::OnPictureUpdated(int process_id,
539 int render_view_id) {
540 CHECK_EQ(web_contents_->GetRenderProcessHost()->GetID(), process_id);
541 if (render_view_id != web_contents_->GetRoutingID())
542 return;
543
544 // TODO(leandrogracia): this can be made unconditional once software rendering
545 // uses Ubercompositor. Until then this path is required for SW invalidations.
546 if (on_new_picture_mode_ == kOnNewPictureEnabled)
547 client_->OnNewPicture(CapturePicture());
548
549 // TODO(leandrogracia): delete when sw rendering uses Ubercompositor.
550 // Invalidation should be provided by the compositor only.
551 Invalidate();
552 }
553
554 void BrowserViewRendererImpl::SetCompositorVisibility(bool visible) {
555 if (compositor_visible_ != visible) {
556 compositor_visible_ = visible;
557 compositor_->SetVisible(compositor_visible_);
558 }
559 }
560
561 void BrowserViewRendererImpl::ResetCompositor() {
562 compositor_.reset(content::Compositor::Create(this));
563 compositor_->SetRootLayer(scissor_clip_layer_);
564 }
565
566 void BrowserViewRendererImpl::Invalidate() {
567 if (view_visible_)
568 client_->Invalidate();
569
570 // When not in invalidation-only mode onNewPicture will be triggered
571 // from the OnPictureUpdated callback.
572 if (on_new_picture_mode_ == kOnNewPictureInvalidationOnly)
573 client_->OnNewPicture(ScopedJavaLocalRef<jobject>());
574 }
575
576 bool BrowserViewRendererImpl::RenderSW(SkCanvas* canvas) {
577 // TODO(leandrogracia): once Ubercompositor is ready and we support software
578 // rendering mode, we should avoid this as much as we can, ideally always.
579 // This includes finding a proper replacement for onDraw calls in hardware
580 // mode with software canvases. http://crbug.com/170086.
581 return RenderPicture(canvas);
582 }
583
584 bool BrowserViewRendererImpl::RenderPicture(SkCanvas* canvas) {
585 skia::RefPtr<SkPicture> picture = GetLastCapturedPicture();
586 if (!picture)
587 return false;
588
589 // Correct device scale.
590 canvas->scale(dpi_scale_, dpi_scale_);
591
592 picture->draw(canvas);
593 return true;
594 }
595
596 } // namespace android_webview
OLDNEW
« no previous file with comments | « android_webview/browser/browser_view_renderer_impl.h ('k') | android_webview/browser/renderer_host/aw_render_view_host_ext.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698