From ab26a9b427ec7c525ccd0025f19f0c91b74d8f6d Mon Sep 17 00:00:00 2001 From: halcanary Date: Wed, 23 Sep 2015 12:40:34 -0700 Subject: 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 --- .gitignore | 2 +- .../android/apps/canvasproof/build.gradle | 37 ++++ .../apps/canvasproof/src/main/AndroidManifest.xml | 29 +++ .../canvasproof/src/main/assets/skps/.gitignore | 1 + .../org/skia/canvasproof/CanvasProofActivity.java | 205 +++++++++++++++++++++ .../org/skia/canvasproof/CreateSkiaPicture.java | 58 ++++++ .../skia/canvasproof/GaneshPictureRenderer.java | 120 ++++++++++++ .../java/org/skia/canvasproof/HwuiPictureView.java | 72 ++++++++ .../canvasproof/src/main/jni/JavaInputStream.cpp | 61 ++++++ .../canvasproof/src/main/jni/JavaInputStream.h | 28 +++ .../jni/org_skia_canvasproof_CreateSkiaPicture.cpp | 42 +++++ .../jni/org_skia_canvasproof_CreateSkiaPicture.h | 35 ++++ .../org_skia_canvasproof_GaneshPictureRenderer.cpp | 146 +++++++++++++++ .../org_skia_canvasproof_GaneshPictureRenderer.h | 52 ++++++ platform_tools/android/apps/settings.gradle | 1 + platform_tools/android/gyp/canvasproof.gypi | 75 ++++++++ platform_tools/android/gyp/skia_android.gypi | 1 + 17 files changed, 964 insertions(+), 1 deletion(-) create mode 100644 platform_tools/android/apps/canvasproof/build.gradle create mode 100644 platform_tools/android/apps/canvasproof/src/main/AndroidManifest.xml create mode 100644 platform_tools/android/apps/canvasproof/src/main/assets/skps/.gitignore create mode 100644 platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CanvasProofActivity.java create mode 100644 platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/CreateSkiaPicture.java create mode 100644 platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/GaneshPictureRenderer.java create mode 100644 platform_tools/android/apps/canvasproof/src/main/java/org/skia/canvasproof/HwuiPictureView.java create mode 100644 platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.cpp create mode 100644 platform_tools/android/apps/canvasproof/src/main/jni/JavaInputStream.h create mode 100644 platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.cpp create mode 100644 platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.h create mode 100644 platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.cpp create mode 100644 platform_tools/android/apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.h create mode 100644 platform_tools/android/gyp/canvasproof.gypi diff --git a/.gitignore b/.gitignore index 7ea825655a..f0e3e29751 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ platform_tools/android/apps/*/build platform_tools/android/apps/*/src/main/libs platform_tools/chromeos/third_party/externals platform_tools/chromeos/toolchain -skps +/skps third_party/externals tools/skp/page_sets/data/*.json tools/skp/page_sets/data/*.wpr 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 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 @@ + + + + + + + + + + + + + + + + + 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(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 +#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(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 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(recorder.endRecordingAsPicture()); + #else + return reinterpret_cast(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 +/* 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 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 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(ptr); + SkPicture* picture = reinterpret_cast(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(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(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(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 +/* 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' diff --git a/platform_tools/android/gyp/canvasproof.gypi b/platform_tools/android/gyp/canvasproof.gypi new file mode 100644 index 0000000000..f733d0e03a --- /dev/null +++ b/platform_tools/android/gyp/canvasproof.gypi @@ -0,0 +1,75 @@ +# Copyright 2015 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'canvasproof', + 'type': 'shared_library', + 'dependencies': [ 'skia_lib.gyp:skia_lib', ], + 'sources': [ + '../apps/canvasproof/src/main/jni/JavaInputStream.cpp', + '../apps/canvasproof/src/main/jni/JavaInputStream.h', + '../apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.cpp', + '../apps/canvasproof/src/main/jni/org_skia_canvasproof_GaneshPictureRenderer.h', + '../apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.cpp', + '../apps/canvasproof/src/main/jni/org_skia_canvasproof_CreateSkiaPicture.h ', + ], + }, + { + 'target_name': 'CopyCanvasProofDeps', + 'type': 'none', + 'dependencies': [ + 'skia_lib.gyp:skia_lib', + 'canvasproof', + ], + 'copies': [ + { + 'destination': '../apps/canvasproof/src/main/libs/<(android_arch)', + 'conditions': [ + [ 'skia_shared_lib', { + 'files': [ + '<(SHARED_LIB_DIR)/libskia_android.so', + '<(SHARED_LIB_DIR)/libcanvasproof.so', + ]}, { + 'files': [ + '<(SHARED_LIB_DIR)/libcanvasproof.so', + ]} + ], + ], + }, + ], + }, + { + 'target_name': 'CanvasProof_APK', + 'type': 'none', + 'dependencies': [ 'CopyCanvasProofDeps', ], + 'actions': [ + { + 'action_name': 'SkiaCanvasProof_apk', + 'inputs': [ + '../apps/canvasproof/src/main/assets/skps', + '../apps/canvasproof/src/main/AndroidManifest.xml', + '../apps/canvasproof/src/main/java/org/skia/canvasproof/CreateSkiaPicture.java', + '../apps/canvasproof/src/main/java/org/skia/canvasproof/CanvasProofActivity.java', + '../apps/canvasproof/src/main/java/org/skia/canvasproof/GaneshPictureRenderer.java', + '../apps/canvasproof/src/main/java/org/skia/canvasproof/HwuiPictureView.java', + '<(android_base)/apps/canvasproof/src/main/libs/<(android_arch)/libcanvasproof.so', + '<(android_base)/apps/canvasproof/src/main/libs/<(android_arch)/libskia_android.so', + + ], + 'outputs': [ + '../apps/canvasproof/build', + ], + 'action': [ + '<(android_base)/apps/gradlew', + ':canvasproof:assemble<(android_variant)Debug', + '-p<(android_base)/apps/canvasproof', + '-PsuppressNativeBuild', + ], + }, + ], + }, + ], +} diff --git a/platform_tools/android/gyp/skia_android.gypi b/platform_tools/android/gyp/skia_android.gypi index 92ee5ddd9d..09a63d50aa 100644 --- a/platform_tools/android/gyp/skia_android.gypi +++ b/platform_tools/android/gyp/skia_android.gypi @@ -45,6 +45,7 @@ }], ], }, + 'includes' : [ 'canvasproof.gypi', ], 'targets': [ { 'target_name': 'CopySampleAppDeps', -- cgit v1.2.3