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/native/java_browser_view_renderer_helper.h"
6
7#include <android/bitmap.h>
8
9#include "android_webview/common/aw_switches.h"
10#include "android_webview/public/browser/draw_sw.h"
11#include "base/debug/trace_event.h"
12#include "jni/JavaBrowserViewRendererHelper_jni.h"
13#include "third_party/skia/include/core/SkBitmap.h"
14#include "third_party/skia/include/utils/SkCanvasStateUtils.h"
15
16using base::android::JavaRef;
17using base::android::ScopedJavaLocalRef;
18
19namespace android_webview {
20
21namespace {
22
23// Provides software rendering functions from the Android glue layer.
24// Allows preventing extra copies of data when rendering.
25AwDrawSWFunctionTable* g_sw_draw_functions = NULL;
26
27class ScopedPixelAccess {
28 public:
29  ScopedPixelAccess(JNIEnv* env, jobject java_canvas) : pixels_(NULL) {
30    if (g_sw_draw_functions && !switches::ForceAuxiliaryBitmap())
31      pixels_ = g_sw_draw_functions->access_pixels(env, java_canvas);
32  }
33
34  ~ScopedPixelAccess() {
35    if (pixels_)
36      g_sw_draw_functions->release_pixels(pixels_);
37  }
38
39  AwPixelInfo* pixels() { return pixels_; }
40
41 private:
42  AwPixelInfo* pixels_;
43
44  DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedPixelAccess);
45};
46
47}  // namespace
48
49// static
50void JavaBrowserViewRendererHelper::SetAwDrawSWFunctionTable(
51    AwDrawSWFunctionTable* table) {
52  g_sw_draw_functions = table;
53}
54
55// static
56JavaBrowserViewRendererHelper* JavaBrowserViewRendererHelper::GetInstance() {
57  static JavaBrowserViewRendererHelper* g_instance =
58      new JavaBrowserViewRendererHelper;
59  return g_instance;
60}
61
62// static
63BrowserViewRendererJavaHelper* BrowserViewRendererJavaHelper::GetInstance() {
64  return JavaBrowserViewRendererHelper::GetInstance();
65}
66
67JavaBrowserViewRendererHelper::JavaBrowserViewRendererHelper() {}
68
69JavaBrowserViewRendererHelper::~JavaBrowserViewRendererHelper() {}
70
71bool JavaBrowserViewRendererHelper::RenderViaAuxilaryBitmapIfNeeded(
72    jobject java_canvas,
73    const gfx::Vector2d& scroll_correction,
74    const gfx::Size& auxiliary_bitmap_size,
75    RenderMethod render_source) {
76  TRACE_EVENT0("android_webview", "RenderViaAuxilaryBitmapIfNeeded");
77
78  JNIEnv* env = base::android::AttachCurrentThread();
79  ScopedPixelAccess auto_release_pixels(env, java_canvas);
80  AwPixelInfo* pixels = auto_release_pixels.pixels();
81  if (pixels && pixels->state) {
82    skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(
83        SkCanvasStateUtils::CreateFromCanvasState(pixels->state));
84
85    // Workarounds for http://crbug.com/271096: SW draw only supports
86    // translate & scale transforms, and a simple rectangular clip.
87    if (canvas && (!canvas->isClipRect() ||
88                   (canvas->getTotalMatrix().getType() &
89                    ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)))) {
90      canvas.clear();
91    }
92    if (canvas) {
93      canvas->translate(scroll_correction.x(), scroll_correction.y());
94      return render_source.Run(canvas.get());
95    }
96  }
97  return RenderViaAuxilaryBitmap(env,
98                                 java_canvas,
99                                 scroll_correction,
100                                 auxiliary_bitmap_size,
101                                 render_source);
102}
103
104bool JavaBrowserViewRendererHelper::RenderViaAuxilaryBitmap(
105    JNIEnv* env,
106    jobject java_canvas,
107    const gfx::Vector2d& scroll_correction,
108    const gfx::Size& auxiliary_bitmap_size,
109    const RenderMethod& render_source) {
110  // Render into an auxiliary bitmap if pixel info is not available.
111  ScopedJavaLocalRef<jobject> jcanvas(env, java_canvas);
112  TRACE_EVENT0("android_webview", "RenderToAuxBitmap");
113
114  if (auxiliary_bitmap_size.width() <= 0 || auxiliary_bitmap_size.height() <= 0)
115    return false;
116
117  ScopedJavaLocalRef<jobject> jbitmap(
118      Java_JavaBrowserViewRendererHelper_createBitmap(
119          env,
120          auxiliary_bitmap_size.width(),
121          auxiliary_bitmap_size.height(),
122          jcanvas.obj()));
123  if (!jbitmap.obj())
124    return false;
125
126  if (!RasterizeIntoBitmap(env,
127                           jbitmap,
128                           render_source)) {
129    return false;
130  }
131
132  Java_JavaBrowserViewRendererHelper_drawBitmapIntoCanvas(
133      env,
134      jbitmap.obj(),
135      jcanvas.obj(),
136      scroll_correction.x(),
137      scroll_correction.y());
138  return true;
139}
140
141bool RegisterJavaBrowserViewRendererHelper(JNIEnv* env) {
142  return RegisterNativesImpl(env);
143}
144
145bool JavaBrowserViewRendererHelper::RasterizeIntoBitmap(
146    JNIEnv* env,
147    const JavaRef<jobject>& jbitmap,
148    const JavaBrowserViewRendererHelper::RenderMethod& renderer) {
149  DCHECK(jbitmap.obj());
150
151  AndroidBitmapInfo bitmap_info;
152  if (AndroidBitmap_getInfo(env, jbitmap.obj(), &bitmap_info) < 0) {
153    LOG(ERROR) << "Error getting java bitmap info.";
154    return false;
155  }
156
157  void* pixels = NULL;
158  if (AndroidBitmap_lockPixels(env, jbitmap.obj(), &pixels) < 0) {
159    LOG(ERROR) << "Error locking java bitmap pixels.";
160    return false;
161  }
162
163  bool succeeded;
164  {
165    SkImageInfo info =
166        SkImageInfo::MakeN32Premul(bitmap_info.width, bitmap_info.height);
167    SkBitmap bitmap;
168    bitmap.installPixels(info, pixels, bitmap_info.stride);
169
170    SkCanvas canvas(bitmap);
171    succeeded = renderer.Run(&canvas);
172  }
173
174  if (AndroidBitmap_unlockPixels(env, jbitmap.obj()) < 0) {
175    LOG(ERROR) << "Error unlocking java bitmap pixels.";
176    return false;
177  }
178
179  return succeeded;
180}
181
182}  // namespace android_webview
183