aboutsummaryrefslogtreecommitdiffhomepage
path: root/platform_tools/android/apps
diff options
context:
space:
mode:
authorGravatar halcanary <halcanary@google.com>2015-09-23 12:40:34 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-09-23 12:40:34 -0700
commitab26a9b427ec7c525ccd0025f19f0c91b74d8f6d (patch)
treefaf1acf3fc409d7a61e23460c960e351bbf7e360 /platform_tools/android/apps
parent3420d2796bad5ce553efb9909790ea72e578dc47 (diff)
android/apps: Add CanvasProof App;
Compare Ganesh and HWUI canvas rendering of SKPs on android. Put SKP files in .../canvasproof/src/main/assets/skps Run on a Marshmallow device. NOTREECHECKS=true Review URL: https://codereview.chromium.org/1258123004
Diffstat (limited to 'platform_tools/android/apps')
-rw-r--r--platform_tools/android/apps/canvasproof/build.gradle37
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/AndroidManifest.xml29
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/assets/skps/.gitignore1
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CanvasProofActivity.java205
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CreateSkiaPicture.java58
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/GaneshPictureRenderer.java120
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/HwuiPictureView.java72
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.cpp61
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.h28
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.cpp42
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.h35
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.cpp146
-rw-r--r--platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.h52
-rw-r--r--platform_tools/android/apps/settings.gradle1
14 files changed, 887 insertions, 0 deletions
diff --git a/platform_tools/android/apps/canvasproof/build.gradle b/platform_tools/android/apps/canvasproof/build.gradle
new file mode 100644
index 0000000000..64463e8103
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/build.gradle
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+apply plugin: 'com.android.application'
+android {
+ compileSdkVersion 19
+ buildToolsVersion "22.0.1"
+ defaultConfig {
+ applicationId "org.skia.canvasproof"
+ minSdkVersion 9
+ targetSdkVersion 19
+ versionCode 1
+ versionName "1.0"
+ signingConfig signingConfigs.debug
+ }
+ sourceSets.main.jni.srcDirs = [] //disable automatic ndk-build call
+ sourceSets.main.jniLibs.srcDir "src/main/libs"
+ productFlavors { arm {}; arm64 {}; x86 {}; x86_64 {}; mips {}; mips64 {}; }
+ applicationVariants.all{ variant ->
+ def buildNativeLib = task("${variant.name}_NativeLib", type:Exec) {
+ workingDir '../../../..' // top-level skia directory
+ commandLine constructBuildCommand(variant, "CopyCanvasProofDeps").split()
+ environment PATH: getPathWithDepotTools()
+ environment ANDROID_SDK_ROOT: getSDKPath()
+ }
+ buildNativeLib.onlyIf { !project.hasProperty("suppressNativeBuild") }
+ TaskCollection<Task> assembleTask
+ assembleTask = project.tasks.matching {
+ it.name.contains("assemble") &&
+ it.name.toLowerCase().endsWith(variant.name.toLowerCase())
+ }
+ assembleTask.getAt(0).dependsOn buildNativeLib
+ }
+}
diff --git a/platform_tools/android/apps/canvasproof/src/main/AndroidManifest.xml b/platform_tools/android/apps/canvasproof/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..be774c880c
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 Google Inc.
+
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+<!-- BEGIN_INCLUDE(manifest) -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.skia.canvasproof"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="9" />
+ <uses-feature android:glEsVersion="0x00020000" />
+ <application android:label="Canvas Proof">
+ <activity android:name="org.skia.canvasproof.CanvasProofActivity"
+ android:label="Canvas Proof"
+ android:configChanges="orientation|keyboardHidden"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+ <meta-data android:name="android.app.lib_name"
+ android:value="canvasproof" />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+<!-- END_INCLUDE(manifest) -->
diff --git a/platform_tools/android/apps/canvasproof/src/main/assets/skps/.gitignore b/platform_tools/android/apps/canvasproof/src/main/assets/skps/.gitignore
new file mode 100644
index 0000000000..4a708012f6
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/assets/skps/.gitignore
@@ -0,0 +1 @@
+*.skp
diff --git a/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CanvasProofActivity.java b/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CanvasProofActivity.java
new file mode 100644
index 0000000000..9363585bbf
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CanvasProofActivity.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+package org.skia.canvasproof;
+
+import android.app.Activity;
+import android.content.res.AssetManager;
+import android.graphics.Picture;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.LinearLayout;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class CanvasProofActivity extends Activity {
+ private static final String TAG = "CanvasProofActivity";
+ private GaneshPictureRenderer ganeshPictureRenderer;
+ private HwuiPictureView hwuiPictureView;
+ private GLSurfaceView ganeshPictureView;
+ private LinearLayout splitPaneView;
+ private View currentView;
+ private float x, y;
+ private int resourcesIndex;
+ private class PictureAsset {
+ public String path;
+ public long ptr;
+ public Picture picture;
+ };
+ private PictureAsset[] assets;
+
+ @SuppressWarnings("deprecation") // purposely using this
+ private static Picture ReadPicture(InputStream inputStream)
+ throws IOException {
+ Picture p = null;
+ try {
+ p = Picture.createFromStream(inputStream);
+ } catch (java.lang.Exception e) {
+ Log.e(TAG, "Exception in Picture.createFromStream", e);
+ }
+ inputStream.close();
+ return p;
+ }
+
+ private void getAssetPaths() {
+ String directory = "skps";
+ AssetManager mgr = this.getAssets();
+ assert (mgr != null);
+ String[] resources;
+ try {
+ resources = mgr.list(directory);
+ } catch (IOException e) {
+ Log.e(TAG, "IOException in getAssetPaths", e);
+ return;
+ }
+ if (resources == null || resources.length == 0) {
+ Log.e(TAG, "SKP assets should be packaged in " +
+ ".../apps/canvasproof/src/main/assets/skps/" +
+ ", but none were found.");
+ return;
+ }
+ CreateSkiaPicture.init();
+ this.assets = new PictureAsset[resources.length];
+ for (int i = 0; i < resources.length; ++i) {
+ String path = directory + File.separator + resources[i];
+ this.assets[i] = new PictureAsset();
+ this.assets[i].path = path;
+ try {
+ this.assets[i].ptr = CreateSkiaPicture.create(mgr.open(path));
+ if (0 == this.assets[i].ptr) {
+ Log.e(TAG, "CreateSkiaPicture.create returned 0 " + path);
+ }
+ Picture p = CanvasProofActivity.ReadPicture(mgr.open(path));
+ if (null == p) {
+ Log.e(TAG, "CanvasProofActivity.ReadPicture.create " +
+ "returned null " + path);
+ } else if (0 == p.getHeight() || 0 == p.getWidth()) {
+ Log.e(TAG, "CanvasProofActivity.ReadPicture.create " +
+ "empty picture" + path);
+ p = null;
+ }
+ this.assets[i].picture = p;
+ } catch (IOException e) {
+ Log.e(TAG, "IOException in getAssetPaths " + path + e);
+ return;
+ }
+ }
+ }
+
+ private void nextView() {
+ if (this.currentView == this.hwuiPictureView) {
+ this.currentView = this.ganeshPictureView;
+ this.ganeshPictureView.setRenderMode(
+ GLSurfaceView.RENDERMODE_CONTINUOUSLY);
+ } else if (this.currentView == null ||
+ this.currentView == this.ganeshPictureView) {
+ this.setContentView(new View(this));
+ LayoutParams layoutParams =
+ new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0.5f);
+ this.ganeshPictureView.setLayoutParams(layoutParams);
+ this.ganeshPictureView.setRenderMode(
+ GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+ this.splitPaneView.addView(this.ganeshPictureView);
+ this.hwuiPictureView.setLayoutParams(layoutParams);
+ this.splitPaneView.addView(this.hwuiPictureView);
+ this.currentView = this.splitPaneView;
+ this.hwuiPictureView.fullTime = false;
+ } else if (this.currentView == this.splitPaneView) {
+ this.splitPaneView.removeAllViews();
+ this.currentView = this.hwuiPictureView;
+ this.hwuiPictureView.fullTime = true;
+ } else {
+ Log.e(TAG, "unexpected value");
+ this.setContentView(null);
+ return;
+ }
+ this.setContentView(this.currentView);
+ this.currentView.invalidate();
+ }
+
+ private void nextPicture(int d) {
+ if (this.assets == null) {
+ Log.w(TAG, "this.assets == null");
+ return;
+ }
+ assert (this.assets.length > 0);
+ resourcesIndex = (resourcesIndex + d) % this.assets.length;
+ while (resourcesIndex < 0) {
+ resourcesIndex += this.assets.length;
+ }
+ while (resourcesIndex >= this.assets.length) {
+ resourcesIndex -= this.assets.length;
+ }
+ this.ganeshPictureRenderer.setPicture(assets[resourcesIndex].ptr);
+ this.hwuiPictureView.setPicture(assets[resourcesIndex].picture);
+ this.currentView.invalidate();
+ }
+
+ @Override
+ protected void onStop() {
+ this.ganeshPictureRenderer.releaseResources();
+ super.onStop();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ this.getAssetPaths();
+ this.ganeshPictureRenderer = new GaneshPictureRenderer();
+ this.hwuiPictureView = new HwuiPictureView(this);
+
+ this.ganeshPictureRenderer.setScale(2.0f);
+ this.hwuiPictureView.setScale(2.0f);
+ this.ganeshPictureView = ganeshPictureRenderer.makeView(this);
+ this.splitPaneView = new LinearLayout(this);
+ this.splitPaneView.setOrientation(LinearLayout.VERTICAL);
+ this.splitPaneView.setGravity(Gravity.FILL);
+
+ LayoutParams layoutParams =
+ new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0.5f);
+ this.ganeshPictureView.setLayoutParams(layoutParams);
+ this.hwuiPictureView.setLayoutParams(layoutParams);
+
+ this.nextView();
+ this.nextPicture(0);
+ }
+
+ // TODO: replace this funtion with onTouchEvent().
+ // @Override public boolean onTouchEvent(MotionEvent event)...
+ @Override
+ public boolean dispatchTouchEvent (MotionEvent event) {
+ switch(event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ this.x = event.getX();
+ this.y = event.getY();
+ break;
+ case MotionEvent.ACTION_UP:
+ float dx = event.getX() - this.x;
+ float dy = event.getY() - this.y;
+ float dx2 = dx * dx;
+ float dy2 = dy * dy;
+ if (dx2 + dy2 > 22500.0) {
+ if (dy2 < dx2) {
+ this.nextPicture(dx > 0 ? 1 : -1);
+ } else if (dy > 0) {
+ this.nextView();
+ }
+ }
+ break;
+ }
+ return super.onTouchEvent(event);
+ }
+}
diff --git a/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CreateSkiaPicture.java b/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CreateSkiaPicture.java
new file mode 100644
index 0000000000..61aa14a7b7
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CreateSkiaPicture.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/*
+AJAR=$ANDROID_SDK_ROOT/platforms/android-19/android.jar
+CLASS=CreateSkiaPicture
+SRC=platform_tools/android/apps/canvasproof/src/main
+javac -classpath $AJAR $SRC/java/org/skia/canvasproof/$CLASS.java
+javah -classpath $AJAR:$SRC/java -d $SRC/jni org.skia.canvasproof.$CLASS
+*/
+
+package org.skia.canvasproof;
+
+import android.util.Log;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.UnsatisfiedLinkError;
+
+public class CreateSkiaPicture {
+ private static final String TAG = "CreateSkiaPicture";
+
+ public static void init() {
+ try {
+ System.loadLibrary("skia_android");
+ System.loadLibrary("canvasproof");
+ } catch (java.lang.Error e) {
+ Log.v(TAG, "System.loadLibrary error", e);
+ }
+ }
+
+ public static long create(InputStream inputStream) throws IOException {
+ byte[] buffer = new byte[16 * (1 << 10)]; // 16 KByte
+ long p = 0;
+ try {
+ p = CreateSkiaPicture.createImpl(inputStream, buffer);
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "UnsatisfiedLinkError createImpl");
+ }
+ inputStream.close();
+ return p;
+ }
+
+ public static void delete(long ptr) {
+ try {
+ if (ptr != 0) {
+ CreateSkiaPicture.deleteImpl(ptr);
+ }
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "UnsatisfiedLinkError deleteImpl");
+ }
+
+ }
+ private static native void deleteImpl(long ptr);
+ private static native long createImpl(InputStream s, byte[] b);
+}
diff --git a/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/GaneshPictureRenderer.java b/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/GaneshPictureRenderer.java
new file mode 100644
index 0000000000..01c6dc3f25
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/GaneshPictureRenderer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// AJAR=$ANDROID_SDK_ROOT/platforms/android-19/android.jar
+// SRC=platform_tools/android/apps/canvasproof/src/main
+// javac -classpath $AJAR $SRC/java/org/skia/canvasproof/GaneshPictureRenderer.java
+// javah -classpath $AJAR:$SRC/java -d $SRC/jni org.skia.canvasproof.GaneshPictureRenderer
+
+package org.skia.canvasproof;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.opengl.GLSurfaceView;
+import android.util.Log;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+public class GaneshPictureRenderer implements GLSurfaceView.Renderer {
+ private static final String TAG = "GaneshPictureRenderer";
+ private long picturePtr;
+ private long contextPtr;
+ private float scale;
+ private int width;
+ private int height;
+ private GLSurfaceView view;
+
+ GaneshPictureRenderer() {
+ try {
+ System.loadLibrary("skia_android");
+ System.loadLibrary("canvasproof");
+ } catch (java.lang.Error e) {
+ Log.e(TAG, "System.loadLibrary error", e);
+ return;
+ }
+ this.scale = 1;
+ }
+ public GLSurfaceView makeView(Activity activity) {
+ this.view = new GLSurfaceView(activity);
+ this.view.setEGLConfigChooser(8, 8, 8, 8, 0, 8);
+ this.view.setEGLContextClientVersion(2);
+ this.view.setRenderer(this);
+ this.view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+ return this.view;
+ }
+ static public Rect cullRect(long picturePtr) {
+ Rect rect = new Rect();
+ try {
+ GaneshPictureRenderer.GetCullRect(rect, picturePtr);
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "GetCullRect failed", e);
+ }
+ return rect;
+ }
+ public void setPicture(long picturePtr) {
+ this.picturePtr = picturePtr;
+ this.view.requestRender();
+ }
+ public void setScale(float s) { this.scale = s; }
+
+ public void releaseResources() {
+ if (this.contextPtr != 0) {
+ try {
+ GaneshPictureRenderer.CleanUp(this.contextPtr);
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "CleanUp failed", e);
+ }
+ }
+ this.contextPtr = 0;
+ }
+
+ private void createContext() {
+ try {
+ this.contextPtr = GaneshPictureRenderer.Ctor();
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "Ctor failed", e);
+ }
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig c) {
+ this.releaseResources();
+ this.createContext();
+ }
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ if (this.contextPtr == 0) {
+ this.createContext();
+ }
+ if (this.width > 0 && this.height > 0 &&
+ this.contextPtr != 0 && this.picturePtr != 0) {
+ try {
+ GaneshPictureRenderer.DrawThisFrame(
+ this.width, this.height, this.scale,
+ this.contextPtr, this.picturePtr);
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "DrawThisFrame failed", e);
+ }
+ }
+ }
+ @Override
+ public void onSurfaceChanged(GL10 gl, int w, int h) {
+ this.width = w;
+ this.height = h;
+ }
+ @Override
+ public void finalize() throws Throwable {
+ super.finalize();
+ this.releaseResources();
+ }
+
+ // Make the native functions static to simplify JNI interaction.
+ private static native void DrawThisFrame(int w, int h, float s, long pr, long pc);
+ private static native long Ctor();
+ private static native void CleanUp(long p);
+ private static native void GetCullRect(Rect r, long picture);
+}
diff --git a/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/HwuiPictureView.java b/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/HwuiPictureView.java
new file mode 100644
index 0000000000..872089c9d3
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/HwuiPictureView.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+package org.skia.canvasproof;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Picture;
+import android.util.Log;
+import android.view.View;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class HwuiPictureView extends View {
+ private static final String TAG = "HwuiPictureView";
+ private Picture picture;
+ private float scale;
+ private Picture defaultPicture;
+
+ public boolean fullTime;
+
+ HwuiPictureView(Context context) {
+ super(context);
+ this.scale = 1.0f;
+ }
+ public void setScale(float s) {
+ this.scale = s;
+ }
+ public void setPicture(Picture p) {
+ this.picture = p;
+ this.invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (this.picture != null) {
+ canvas.save();
+ canvas.scale(scale, scale);
+ HwuiPictureView.draw(canvas, this.picture);
+ canvas.restore();
+ if (fullTime) {
+ this.invalidate();
+ }
+ }
+ }
+
+ static private void draw(Canvas canvas, Picture p) {
+ if (android.os.Build.VERSION.SDK_INT > 22) {
+ try {
+ canvas.drawPicture(p);
+ return;
+ } catch (java.lang.Exception e) {
+ Log.e(TAG, "Exception while drawing picture in Hwui");
+ }
+ }
+ if (p.getWidth() > 0 && p.getHeight() > 0) {
+ // Fallback to software rendering.
+ Bitmap bm = Bitmap.createBitmap(p.getWidth(), p.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ (new Canvas(bm)).drawPicture(p);
+ canvas.drawBitmap(bm, 0.0f, 0.0f, null);
+ bm.recycle();
+ }
+ }
+}
diff --git a/platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.cpp b/platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.cpp
new file mode 100644
index 0000000000..823b72f7ef
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "JavaInputStream.h"
+
+JavaInputStream::JavaInputStream(
+ JNIEnv* env, jbyteArray javaBuffer, jobject inputStream)
+ : fEnv(env)
+ , fStartIndex(0)
+ , fEndIndex(0) {
+ SkASSERT(inputStream);
+ SkASSERT(javaBuffer);
+ fInputStream = inputStream;
+ fJavaBuffer = javaBuffer;
+ fInputStreamClass = env->FindClass("java/io/InputStream");
+ SkASSERT(fInputStreamClass);
+ fReadMethodID = env->GetMethodID(fInputStreamClass, "read", "([B)I");
+ SkASSERT(fReadMethodID);
+}
+
+bool JavaInputStream::isAtEnd() const { return fStartIndex == fEndIndex; }
+
+size_t JavaInputStream::read(void* voidBuffer, size_t size) {
+ size_t totalRead = 0;
+ char* buffer = static_cast<char*>(voidBuffer); // may be NULL;
+ while (size) {
+ // make sure the cache has at least one byte or is done.
+ if (fStartIndex == fEndIndex) {
+ jint count =
+ fEnv->CallIntMethod(fInputStream, fReadMethodID, fJavaBuffer);
+ if (fEnv->ExceptionCheck()) {
+ fEnv->ExceptionDescribe();
+ fEnv->ExceptionClear();
+ SkDebugf("---- java.io.InputStream::read() threw an exception\n");
+ return 0;
+ }
+ fStartIndex = 0;
+ fEndIndex = count;
+ if (this->isAtEnd()) {
+ return totalRead; // No more to read.
+ }
+ }
+ SkASSERT(fEndIndex > fStartIndex);
+ size_t length = SkTMin(SkToSizeT(fEndIndex - fStartIndex), size);
+ if (buffer && length) {
+ jbyte* bufferElements
+ = fEnv->GetByteArrayElements(fJavaBuffer, NULL);
+ memcpy(buffer, &bufferElements[fStartIndex], length);
+ buffer += length;
+ fEnv->ReleaseByteArrayElements(fJavaBuffer, bufferElements, 0);
+ }
+ totalRead += length;
+ size -= length;
+ fStartIndex += length;
+ }
+ return totalRead;
+}
diff --git a/platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.h b/platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.h
new file mode 100644
index 0000000000..e14e026552
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef JavaInputStream_DEFINED
+#define JavaInputStream_DEFINED
+
+#include <jni.h>
+#include "SkStream.h"
+
+class JavaInputStream : public SkStream {
+public:
+ JavaInputStream(JNIEnv*, jbyteArray javaBuffer, jobject javaIoInputStream);
+ bool isAtEnd() const override;
+ size_t read(void*, size_t) override;
+private:
+ JNIEnv* fEnv;
+ jobject fInputStream;
+ jbyteArray fJavaBuffer;
+ jclass fInputStreamClass;
+ jmethodID fReadMethodID;
+ jint fStartIndex;
+ jint fEndIndex;
+};
+
+#endif // JavaInputStream_DEFINED
diff --git a/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.cpp b/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.cpp
new file mode 100644
index 0000000000..c5d9759e54
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "org_skia_canvasproof_CreateSkiaPicture.h"
+#include "JavaInputStream.h"
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
+
+/*
+ * Class: org_skia_canvasproof_CreateSkiaPicture
+ * Method: delete
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_skia_canvasproof_CreateSkiaPicture_deleteImpl(
+ JNIEnv* env, jclass clazz, jlong ptr) {
+ SkSafeUnref(reinterpret_cast<SkPicture*>(ptr));
+}
+
+/*
+ * Class: org_skia_canvasproof_CreateSkiaPicture
+ * Method: createImpl
+ * Signature: (Ljava/io/InputStream;[B)J
+ */
+JNIEXPORT jlong JNICALL Java_org_skia_canvasproof_CreateSkiaPicture_createImpl
+ (JNIEnv* env, jclass clazz, jobject inputStream, jbyteArray buffer) {
+ JavaInputStream stream(env, buffer, inputStream);
+ #if 0
+ SkAutoTUnref<SkPicture> p(SkPicture::CreateFromStream(&stream));
+ if (!p) { return 0; }
+ SkPictureRecorder recorder;
+ SkRect bounds = p->cullRect();
+ SkRTreeFactory bbh;
+ recorder.beginRecording(bounds, &bbh)->drawPicture(p);
+ return reinterpret_cast<long>(recorder.endRecordingAsPicture());
+ #else
+ return reinterpret_cast<long>(SkPicture::CreateFromStream(&stream));
+ #endif
+}
diff --git a/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.h b/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.h
new file mode 100644
index 0000000000..2937d549ef
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_skia_canvasproof_CreateSkiaPicture */
+
+#ifndef _Included_org_skia_canvasproof_CreateSkiaPicture
+#define _Included_org_skia_canvasproof_CreateSkiaPicture
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_skia_canvasproof_CreateSkiaPicture
+ * Method: deleteImpl
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_skia_canvasproof_CreateSkiaPicture_deleteImpl
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_skia_canvasproof_CreateSkiaPicture
+ * Method: createImpl
+ * Signature: (Ljava/io/InputStream;[B)J
+ */
+JNIEXPORT jlong JNICALL Java_org_skia_canvasproof_CreateSkiaPicture_createImpl
+ (JNIEnv *, jclass, jobject, jbyteArray);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.cpp b/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.cpp
new file mode 100644
index 0000000000..d1de529674
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "org_skia_canvasproof_GaneshPictureRenderer.h"
+
+#include "GrContext.h"
+#include "JavaInputStream.h"
+#include "SkCanvas.h"
+#include "SkMatrix.h"
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkStream.h"
+#include "SkSurface.h"
+
+#define TAG "GaneshPictureRenderer.cpp: "
+
+static void render_picture(GrContext* grContext,
+ int width,
+ int height,
+ const SkPicture* picture,
+ const SkMatrix& matrix) {
+ SkASSERT(grContext);
+ if (!picture) {
+ SkDebugf(TAG "!picture\n");
+ return;
+ }
+ // Render to the default framebuffer render target.
+ GrBackendRenderTargetDesc desc;
+ desc.fWidth = width;
+ desc.fHeight = height;
+ desc.fConfig = kSkia8888_GrPixelConfig;
+ desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+ SkSurfaceProps surfaceProps(SkSurfaceProps::kUseDeviceIndependentFonts_Flag,
+ kUnknown_SkPixelGeometry);
+ // TODO: Check to see if we can keep the surface between draw calls.
+ SkAutoTUnref<SkSurface> surface(
+ SkSurface::NewFromBackendRenderTarget(
+ grContext, desc, &surfaceProps));
+ if (surface) {
+ SkCanvas* canvas = surface->getCanvas();
+ SkASSERT(canvas);
+ canvas->clear(SK_ColorGRAY);
+ canvas->concat(matrix);
+ SkRect cullRect = picture->cullRect();
+ canvas->clipRect(cullRect);
+ picture->playback(canvas);
+ canvas->flush();
+ }
+}
+
+namespace {
+struct GaneshPictureRendererImpl {
+ SkAutoTUnref<GrContext> fGrContext;
+ void render(int w, int h, const SkPicture* p, const SkMatrix& m) {
+ if (!fGrContext) {
+ // Cache the rendering context between frames.
+ fGrContext.reset(GrContext::Create(kOpenGL_GrBackend, 0));
+ if (!fGrContext) {
+ SkDebugf(TAG "GrContext::Create - failed\n");
+ return;
+ }
+ }
+ render_picture(fGrContext, w, h, p, m);
+ }
+};
+} // namespace
+
+/*
+ * Class: org_skia_canvasproof_GaneshPictureRenderer
+ * Method: DrawThisFrame
+ * Signature: (IIFJ)V
+ */
+JNIEXPORT void JNICALL Java_org_skia_canvasproof_GaneshPictureRenderer_DrawThisFrame(
+ JNIEnv*, jclass, jint width, jint height, jfloat scale, jlong ptr, jlong pic) {
+ if (!ptr) { return; }
+ SkMatrix matrix = SkMatrix::MakeScale((SkScalar)scale);
+ GaneshPictureRendererImpl* impl =
+ reinterpret_cast<GaneshPictureRendererImpl*>(ptr);
+ SkPicture* picture = reinterpret_cast<SkPicture*>(pic);
+ impl->render((int)width, (int)height, picture, matrix);
+}
+
+/*
+ * Class: org_skia_canvasproof_GaneshPictureRenderer
+ * Method: Ctor
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_org_skia_canvasproof_GaneshPictureRenderer_Ctor
+ (JNIEnv *, jclass) {
+ return reinterpret_cast<jlong>(new GaneshPictureRendererImpl);
+}
+
+/*
+ * Class: org_skia_canvasproof_GaneshPictureRenderer
+ * Method: CleanUp
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_skia_canvasproof_GaneshPictureRenderer_CleanUp
+ (JNIEnv *, jclass, jlong ptr) {
+ delete reinterpret_cast<GaneshPictureRendererImpl*>(ptr);
+}
+
+namespace {
+struct AndroidRectHelper {
+ jfieldID fLeft, fTop, fRight, fBottom;
+ AndroidRectHelper()
+ : fLeft(nullptr), fTop(nullptr), fRight(nullptr), fBottom(nullptr) {}
+ void config(JNIEnv *env) {
+ if (!fLeft) {
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ SkASSERT(rectClass);
+ fLeft = env->GetFieldID(rectClass, "left", "I");
+ fTop = env->GetFieldID(rectClass, "top", "I");
+ fRight = env->GetFieldID(rectClass, "right", "I");
+ fBottom = env->GetFieldID(rectClass, "bottom", "I");
+ }
+ }
+};
+} // namespace
+
+/*
+ * Class: org_skia_canvasproof_GaneshPictureRenderer
+ * Method: GetCullRect
+ * Signature: (Landroid/graphics/Rect;J)V
+ */
+JNIEXPORT void JNICALL Java_org_skia_canvasproof_GaneshPictureRenderer_GetCullRect
+ (JNIEnv *env, jclass, jobject androidGraphicsRect, jlong picturePtr) {
+ SkASSERT(androidGraphicsRect);
+ const SkPicture* picture = reinterpret_cast<SkPicture*>(picturePtr);
+ SkRect rect = SkRect::MakeEmpty();
+ if (picture) {
+ rect = picture->cullRect();
+ }
+ SkIRect iRect;
+ rect.roundOut(&iRect);
+ static AndroidRectHelper help;
+ help.config(env);
+ env->SetIntField(androidGraphicsRect, help.fLeft, (jint)(iRect.left()));
+ env->SetIntField(androidGraphicsRect, help.fTop, (jint)(iRect.top()));
+ env->SetIntField(androidGraphicsRect, help.fRight, (jint)(iRect.right()));
+ env->SetIntField(androidGraphicsRect, help.fBottom, (jint)(iRect.bottom()));
+}
diff --git a/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.h b/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.h
new file mode 100644
index 0000000000..401fa87872
--- /dev/null
+++ b/platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_skia_canvasproof_GaneshPictureRenderer */
+
+#ifndef _Included_org_skia_canvasproof_GaneshPictureRenderer
+#define _Included_org_skia_canvasproof_GaneshPictureRenderer
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_skia_canvasproof_GaneshPictureRenderer
+ * Method: DrawThisFrame
+ * Signature: (IIFJJ)V
+ */
+JNIEXPORT void JNICALL Java_org_skia_canvasproof_GaneshPictureRenderer_DrawThisFrame
+ (JNIEnv *, jclass, jint, jint, jfloat, jlong, jlong);
+
+/*
+ * Class: org_skia_canvasproof_GaneshPictureRenderer
+ * Method: Ctor
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_org_skia_canvasproof_GaneshPictureRenderer_Ctor
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_skia_canvasproof_GaneshPictureRenderer
+ * Method: CleanUp
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_skia_canvasproof_GaneshPictureRenderer_CleanUp
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_skia_canvasproof_GaneshPictureRenderer
+ * Method: GetCullRect
+ * Signature: (Landroid/graphics/Rect;J)V
+ */
+JNIEXPORT void JNICALL Java_org_skia_canvasproof_GaneshPictureRenderer_GetCullRect
+ (JNIEnv *, jclass, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/platform_tools/android/apps/settings.gradle b/platform_tools/android/apps/settings.gradle
index 89cc95447b..787bcf06cc 100644
--- a/platform_tools/android/apps/settings.gradle
+++ b/platform_tools/android/apps/settings.gradle
@@ -1,2 +1,3 @@
include ':sample_app'
include ':visualbench'
+include ':canvasproof'