summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorskyostil@chromium.org <skyostil@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-12 16:10:22 +0000
committerskyostil@chromium.org <skyostil@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-12 16:10:22 +0000
commit9e3add14706048f5f3ac5973025fea6e058f0453 (patch)
treea22dbb00360f38f8c50769d3c5276891edb58e2e
parentf2f56a162140fcc18a57c9c02d9ecb89d750fc62 (diff)
downloadchromium_src-9e3add14706048f5f3ac5973025fea6e058f0453.zip
chromium_src-9e3add14706048f5f3ac5973025fea6e058f0453.tar.gz
chromium_src-9e3add14706048f5f3ac5973025fea6e058f0453.tar.bz2
Implement window and view snapshots on Android
This patch implements Ui::GrabViewSnapshot and Ui::GrabWindowSnapshot on Android. They both return a screen capture of the requested content region. As on other platforms the URL bar will not be included in the result, but note that any UI elements overlapping with the content (e.g., the toolbar drop shadow) will be shown. BUG=242299 TEST=tools/telemetry/run_tests --browser=android-chrome GpuTabTest -v -v -v Review URL: https://chromiumcodereview.appspot.com/23632006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@222789 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/browser/android/content_view_core_impl.cc4
-rw-r--r--ui/android/java/src/org/chromium/ui/UiUtils.java83
-rw-r--r--ui/android/java/src/org/chromium/ui/WindowAndroid.java43
-rw-r--r--ui/android/window_android.cc17
-rw-r--r--ui/android/window_android.h12
-rw-r--r--ui/snapshot/snapshot_android.cc18
6 files changed, 173 insertions, 4 deletions
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index 688e1ec..669c896 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -375,6 +375,10 @@ void ContentViewCoreImpl::UpdateFrameInfo(
const gfx::Vector2dF& controls_offset,
const gfx::Vector2dF& content_offset,
float overdraw_bottom_height) {
+ if (window_android_)
+ window_android_->set_content_offset(
+ gfx::ScaleVector2d(content_offset, dpi_scale_));
+
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
diff --git a/ui/android/java/src/org/chromium/ui/UiUtils.java b/ui/android/java/src/org/chromium/ui/UiUtils.java
index 4a9fe15..c11d9e3 100644
--- a/ui/android/java/src/org/chromium/ui/UiUtils.java
+++ b/ui/android/java/src/org/chromium/ui/UiUtils.java
@@ -5,7 +5,11 @@
package org.chromium.ui;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Rect;
+import android.util.Log;
+import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
@@ -15,6 +19,8 @@ import android.view.inputmethod.InputMethodManager;
* This class is not supposed to be instantiated.
*/
public class UiUtils {
+ private static final String TAG = "UiUtils";
+
/**
* Guards this class from being instantiated.
*/
@@ -96,4 +102,81 @@ public class UiUtils {
container.addView(newView, index);
return index;
}
+
+ /**
+ * Generates a scaled screenshot of the given view. The maximum size of the screenshot is
+ * determined by maximumDimension.
+ *
+ * @param currentView The view to generate a screenshot of.
+ * @param maximumDimension The maximum width or height of the generated screenshot. The bitmap
+ * will be scaled to ensure the maximum width or height is equal to or
+ * less than this. Any value <= 0, will result in no scaling.
+ * @param bitmapConfig Bitmap config for the generated screenshot (ARGB_8888 or RGB_565).
+ * @return The screen bitmap of the view or null if a problem was encountered.
+ */
+ public static Bitmap generateScaledScreenshot(
+ View currentView, int maximumDimension, Bitmap.Config bitmapConfig) {
+ Bitmap screenshot = null;
+ boolean drawingCacheEnabled = currentView.isDrawingCacheEnabled();
+ try {
+ prepareViewHierarchyForScreenshot(currentView, true);
+ if (!drawingCacheEnabled) currentView.setDrawingCacheEnabled(true);
+ // Android has a maximum drawing cache size and if the drawing cache is bigger
+ // than that, getDrawingCache() returns null.
+ Bitmap originalBitmap = currentView.getDrawingCache();
+ if (originalBitmap != null) {
+ double originalHeight = originalBitmap.getHeight();
+ double originalWidth = originalBitmap.getWidth();
+ int newWidth = (int) originalWidth;
+ int newHeight = (int) originalHeight;
+ if (maximumDimension > 0) {
+ double scale = maximumDimension / Math.max(originalWidth, originalHeight);
+ newWidth = (int) Math.round(originalWidth * scale);
+ newHeight = (int) Math.round(originalHeight * scale);
+ }
+ Bitmap scaledScreenshot =
+ Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, true);
+ if (scaledScreenshot.getConfig() != bitmapConfig) {
+ screenshot = scaledScreenshot.copy(bitmapConfig, false);
+ scaledScreenshot.recycle();
+ scaledScreenshot = null;
+ } else {
+ screenshot = scaledScreenshot;
+ }
+ } else if (currentView.getMeasuredHeight() > 0 && currentView.getMeasuredWidth() > 0) {
+ double originalHeight = currentView.getMeasuredHeight();
+ double originalWidth = currentView.getMeasuredWidth();
+ int newWidth = (int) originalWidth;
+ int newHeight = (int) originalHeight;
+ if (maximumDimension > 0) {
+ double scale = maximumDimension / Math.max(originalWidth, originalHeight);
+ newWidth = (int) Math.round(originalWidth * scale);
+ newHeight = (int) Math.round(originalHeight * scale);
+ }
+ Bitmap bitmap = Bitmap.createBitmap(newWidth, newHeight, bitmapConfig);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.scale((float) (newWidth / originalWidth),
+ (float) (newHeight / originalHeight));
+ currentView.draw(canvas);
+ screenshot = bitmap;
+ }
+ } catch (OutOfMemoryError e) {
+ Log.d(TAG, "Unable to capture screenshot and scale it down." + e.getMessage());
+ } finally {
+ if (!drawingCacheEnabled) currentView.setDrawingCacheEnabled(false);
+ prepareViewHierarchyForScreenshot(currentView, false);
+ }
+ return screenshot;
+ }
+
+ private static void prepareViewHierarchyForScreenshot(View view, boolean takingScreenshot) {
+ if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ prepareViewHierarchyForScreenshot(viewGroup.getChildAt(i), takingScreenshot);
+ }
+ } else if (view instanceof SurfaceView) {
+ view.setWillNotDraw(!takingScreenshot);
+ }
+ }
}
diff --git a/ui/android/java/src/org/chromium/ui/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/WindowAndroid.java
index c6b263ca..0707eaf 100644
--- a/ui/android/java/src/org/chromium/ui/WindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/WindowAndroid.java
@@ -9,12 +9,18 @@ import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.Bundle;
+import android.util.Log;
import android.util.SparseArray;
+import android.view.View;
import android.widget.Toast;
+import java.io.ByteArrayOutputStream;
import java.util.HashMap;
+import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
/**
@@ -23,6 +29,8 @@ import org.chromium.base.JNINamespace;
@JNINamespace("ui")
public class WindowAndroid {
+ private static final String TAG = "WindowAndroid";
+
// Native pointer to the c++ WindowAndroid object.
private int mNativeWindowAndroid = 0;
@@ -210,6 +218,41 @@ public class WindowAndroid {
return mNativeWindowAndroid;
}
+ /**
+ * Returns a PNG-encoded screenshot of the the window region at (|windowX|,
+ * |windowY|) with the size |width| by |height| pixels.
+ */
+ @CalledByNative
+ public byte[] grabSnapshot(int windowX, int windowY, int width, int height) {
+ try {
+ // Take a screenshot of the content view. This generally includes UI
+ // controls such as the URL bar.
+ View contentView = mActivity.findViewById(android.R.id.content);
+ if (contentView == null) return null;
+ Bitmap bitmap =
+ UiUtils.generateScaledScreenshot(contentView, 0, Bitmap.Config.ARGB_8888);
+ if (bitmap == null) return null;
+
+ // Clip the result into the requested region.
+ if (windowX > 0 || windowY > 0 || width != bitmap.getWidth() ||
+ height != bitmap.getHeight()) {
+ Rect clip = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ clip.intersect(windowX, windowY, windowX + width, windowY + height);
+ bitmap = Bitmap.createBitmap(
+ bitmap, clip.left, clip.top, clip.width(), clip.height());
+ }
+
+ // Compress the result into a PNG.
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, result)) return null;
+ bitmap.recycle();
+ return result.toByteArray();
+ } catch (OutOfMemoryError e) {
+ Log.e(TAG, "Out of memory while grabbing window snapshot.", e);
+ return null;
+ }
+ }
+
private native int nativeInit();
private native void nativeDestroy(int nativeWindowAndroid);
diff --git a/ui/android/window_android.cc b/ui/android/window_android.cc
index 1f2ccb2f9..439268f 100644
--- a/ui/android/window_android.cc
+++ b/ui/android/window_android.cc
@@ -5,6 +5,7 @@
#include "ui/android/window_android.h"
#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
#include "base/android/jni_helper.h"
#include "base/android/scoped_java_ref.h"
#include "jni/WindowAndroid_jni.h"
@@ -33,6 +34,22 @@ bool WindowAndroid::RegisterWindowAndroid(JNIEnv* env) {
WindowAndroid::~WindowAndroid() {
}
+bool WindowAndroid::GrabSnapshot(
+ int content_x, int content_y, int width, int height,
+ std::vector<unsigned char>* png_representation) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jbyteArray> result =
+ Java_WindowAndroid_grabSnapshot(env, GetJavaObject().obj(),
+ content_x + content_offset_.x(),
+ content_y + content_offset_.y(),
+ width, height);
+ if (result.is_null())
+ return false;
+ base::android::JavaByteArrayToByteVector(
+ env, result.obj(), png_representation);
+ return true;
+}
+
// ----------------------------------------------------------------------------
// Native JNI methods
// ----------------------------------------------------------------------------
diff --git a/ui/android/window_android.h b/ui/android/window_android.h
index c1c5c44..14b1f09 100644
--- a/ui/android/window_android.h
+++ b/ui/android/window_android.h
@@ -6,9 +6,11 @@
#define UI_ANDROID_WINDOW_ANDROID_H_
#include <jni.h>
+#include <vector>
#include "base/android/jni_helper.h"
#include "base/android/scoped_java_ref.h"
#include "ui/base/ui_export.h"
+#include "ui/gfx/vector2d_f.h"
namespace ui {
@@ -23,10 +25,20 @@ class UI_EXPORT WindowAndroid {
static bool RegisterWindowAndroid(JNIEnv* env);
+ // The content offset is used to translate snapshots to the correct part of
+ // the window.
+ void set_content_offset(const gfx::Vector2dF& content_offset) {
+ content_offset_ = content_offset;
+ }
+
+ bool GrabSnapshot(int content_x, int content_y, int width, int height,
+ std::vector<unsigned char>* png_representation);
+
private:
~WindowAndroid();
JavaObjectWeakGlobalRef weak_java_window_;
+ gfx::Vector2dF content_offset_;
DISALLOW_COPY_AND_ASSIGN(WindowAndroid);
};
diff --git a/ui/snapshot/snapshot_android.cc b/ui/snapshot/snapshot_android.cc
index 4e89c9d..14c43ee 100644
--- a/ui/snapshot/snapshot_android.cc
+++ b/ui/snapshot/snapshot_android.cc
@@ -4,22 +4,32 @@
#include "ui/snapshot/snapshot.h"
+#include "ui/android/view_android.h"
+#include "ui/android/window_android.h"
+#include "ui/gfx/display.h"
#include "ui/gfx/rect.h"
+#include "ui/gfx/screen.h"
namespace ui {
bool GrabViewSnapshot(gfx::NativeView view,
std::vector<unsigned char>* png_representation,
const gfx::Rect& snapshot_bounds) {
- // TODO(bajones): Implement Android snapshot functionality
- return false;
+ return GrabWindowSnapshot(
+ view->GetWindowAndroid(), png_representation, snapshot_bounds);
}
bool GrabWindowSnapshot(gfx::NativeWindow window,
std::vector<unsigned char>* png_representation,
const gfx::Rect& snapshot_bounds) {
- // TODO(bajones): Implement Android snapshot functionality
- return false;
+ gfx::Display display =
+ gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
+ gfx::Rect scaled_bounds =
+ gfx::ScaleToEnclosingRect(snapshot_bounds,
+ display.device_scale_factor());
+ return window->GrabSnapshot(
+ scaled_bounds.x(), scaled_bounds.y(), scaled_bounds.width(),
+ scaled_bounds.height(), png_representation);
}
} // namespace ui