// 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/native/java_browser_view_renderer_helper.h" #include #include "android_webview/public/browser/draw_sw.h" #include "base/debug/trace_event.h" #include "jni/JavaBrowserViewRendererHelper_jni.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/utils/SkCanvasStateUtils.h" using base::android::JavaRef; using base::android::ScopedJavaLocalRef; namespace android_webview { namespace { // Provides software rendering functions from the Android glue layer. // Allows preventing extra copies of data when rendering. AwDrawSWFunctionTable* g_sw_draw_functions = NULL; class ScopedPixelAccess { public: ScopedPixelAccess(JNIEnv* env, jobject java_canvas) : pixels_(NULL) { if (g_sw_draw_functions) pixels_ = g_sw_draw_functions->access_pixels(env, java_canvas); } ~ScopedPixelAccess() { if (pixels_) g_sw_draw_functions->release_pixels(pixels_); } AwPixelInfo* pixels() { return pixels_; } private: AwPixelInfo* pixels_; DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedPixelAccess); }; } // namespace // static void JavaBrowserViewRendererHelper::SetAwDrawSWFunctionTable( AwDrawSWFunctionTable* table) { g_sw_draw_functions = table; } // static JavaBrowserViewRendererHelper* JavaBrowserViewRendererHelper::GetInstance() { static JavaBrowserViewRendererHelper* g_instance = new JavaBrowserViewRendererHelper; return g_instance; } // static BrowserViewRendererJavaHelper* BrowserViewRendererJavaHelper::GetInstance() { return JavaBrowserViewRendererHelper::GetInstance(); } JavaBrowserViewRendererHelper::JavaBrowserViewRendererHelper() {} JavaBrowserViewRendererHelper::~JavaBrowserViewRendererHelper() {} bool JavaBrowserViewRendererHelper::RenderViaAuxilaryBitmapIfNeeded( jobject java_canvas, const gfx::Vector2d& scroll_correction, const gfx::Rect& clip, RenderMethod render_source) { TRACE_EVENT0("android_webview", "RenderViaAuxilaryBitmapIfNeeded"); JNIEnv* env = base::android::AttachCurrentThread(); ScopedPixelAccess auto_release_pixels(env, java_canvas); AwPixelInfo* pixels = auto_release_pixels.pixels(); if (pixels && pixels->state) { skia::RefPtr canvas = skia::AdoptRef( SkCanvasStateUtils::CreateFromCanvasState(pixels->state)); // Workarounds for http://crbug.com/271096: SW draw only supports // translate & scale transforms, and a simple rectangular clip. if (canvas && (!canvas->getTotalClip().isRect() || (canvas->getTotalMatrix().getType() & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)))) { canvas.clear(); } if (canvas) { canvas->translate(scroll_correction.x(), scroll_correction.y()); return render_source.Run(canvas.get()); } } return RenderViaAuxilaryBitmap( env, java_canvas, scroll_correction, clip, render_source); } bool JavaBrowserViewRendererHelper::RenderViaAuxilaryBitmap( JNIEnv* env, jobject java_canvas, const gfx::Vector2d& scroll_correction, const gfx::Rect& clip, const RenderMethod& render_source) { // Render into an auxiliary bitmap if pixel info is not available. ScopedJavaLocalRef jcanvas(env, java_canvas); TRACE_EVENT0("android_webview", "RenderToAuxBitmap"); if (clip.width() <= 0 || clip.height() <= 0) return false; ScopedJavaLocalRef jbitmap( Java_JavaBrowserViewRendererHelper_createBitmap( env, clip.width(), clip.height(), jcanvas.obj())); if (!jbitmap.obj()) return false; if (!RasterizeIntoBitmap(env, jbitmap, clip.x() - scroll_correction.x(), clip.y() - scroll_correction.y(), render_source)) { return false; } Java_JavaBrowserViewRendererHelper_drawBitmapIntoCanvas( env, jbitmap.obj(), jcanvas.obj(), clip.x(), clip.y()); return true; } bool RegisterJavaBrowserViewRendererHelper(JNIEnv* env) { return RegisterNativesImpl(env) >= 0; } bool JavaBrowserViewRendererHelper::RasterizeIntoBitmap( JNIEnv* env, const JavaRef& jbitmap, int scroll_x, int scroll_y, const JavaBrowserViewRendererHelper::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; { SkImageInfo info = SkImageInfo::MakeN32Premul(bitmap_info.width, bitmap_info.height); SkBitmap bitmap; bitmap.installPixels(info, pixels, bitmap_info.stride); SkCanvas canvas(bitmap); 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 android_webview