summaryrefslogtreecommitdiffstats
path: root/tests/CanvasCompare
diff options
context:
space:
mode:
authorChris Craik <ccraik@google.com>2012-10-01 18:22:38 -0700
committerChris Craik <ccraik@google.com>2012-11-20 12:41:19 -0800
commitc3683b552f592d8039a466c663f7de8c8286e975 (patch)
treef7685a25563d42394739ab768e3daa02248b47af /tests/CanvasCompare
parent74e6489d8910cfa5354b5ac35ffb4ac5968ebe62 (diff)
downloadframeworks_base-c3683b552f592d8039a466c663f7de8c8286e975.zip
frameworks_base-c3683b552f592d8039a466c663f7de8c8286e975.tar.gz
frameworks_base-c3683b552f592d8039a466c663f7de8c8286e975.tar.bz2
Hardware / Software Canvas comparison tool
Has automated and manual testing modes Change-Id: I84d27447ad64021540525372022ab13a36ffc116
Diffstat (limited to 'tests/CanvasCompare')
-rw-r--r--tests/CanvasCompare/Android.mk28
-rw-r--r--tests/CanvasCompare/AndroidManifest.xml47
-rw-r--r--tests/CanvasCompare/res/drawable/sunset1.jpgbin0 -> 25019 bytes
-rw-r--r--tests/CanvasCompare/res/layout/automatic_layout.xml38
-rw-r--r--tests/CanvasCompare/res/layout/manual_layout.xml119
-rw-r--r--tests/CanvasCompare/res/values/strings.xml31
-rw-r--r--tests/CanvasCompare/res/values/values.xml25
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java136
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java122
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java469
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java190
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java56
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java211
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java111
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java46
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs56
16 files changed, 1685 insertions, 0 deletions
diff --git a/tests/CanvasCompare/Android.mk b/tests/CanvasCompare/Android.mk
new file mode 100644
index 0000000..642c9e9
--- /dev/null
+++ b/tests/CanvasCompare/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := CanvasCompare
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+include $(BUILD_PACKAGE)
diff --git a/tests/CanvasCompare/AndroidManifest.xml b/tests/CanvasCompare/AndroidManifest.xml
new file mode 100644
index 0000000..1cb8c37
--- /dev/null
+++ b/tests/CanvasCompare/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.hwuicompare" >
+
+ <!-- for perfhud -->
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.Holo.Light.NoActionBar">
+ <activity
+ android:name="AutomaticActivity"
+ android:label="CanvasAutoCompare" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name="ManualActivity"
+ android:label="CanvasManualCompare" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation
+ android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.test.hwuicompare"
+ android:label="HW/SW Canvas comparison tool."/>
+
+</manifest>
diff --git a/tests/CanvasCompare/res/drawable/sunset1.jpg b/tests/CanvasCompare/res/drawable/sunset1.jpg
new file mode 100644
index 0000000..92851f3
--- /dev/null
+++ b/tests/CanvasCompare/res/drawable/sunset1.jpg
Binary files differ
diff --git a/tests/CanvasCompare/res/layout/automatic_layout.xml b/tests/CanvasCompare/res/layout/automatic_layout.xml
new file mode 100644
index 0000000..e049ec0
--- /dev/null
+++ b/tests/CanvasCompare/res/layout/automatic_layout.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <com.android.test.hwuicompare.MainView
+ android:id="@+id/hardware_view"
+ android:layout_width="@dimen/layer_width"
+ android:layout_height="@dimen/layer_width" />
+
+ <ImageView
+ android:id="@+id/software_image_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true" />
+
+ <ImageView
+ android:id="@+id/hardware_image_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true" />
+
+</RelativeLayout>
diff --git a/tests/CanvasCompare/res/layout/manual_layout.xml b/tests/CanvasCompare/res/layout/manual_layout.xml
new file mode 100644
index 0000000..1a9288c
--- /dev/null
+++ b/tests/CanvasCompare/res/layout/manual_layout.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical" >
+
+ <HorizontalScrollView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:id="@+id/spinner_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" />
+ </HorizontalScrollView>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:baselineAligned="true"
+ android:orientation="horizontal" >
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:orientation="horizontal" >
+
+ <com.android.test.hwuicompare.MainView
+ android:id="@+id/hardware_view"
+ android:layout_width="@dimen/layer_width"
+ android:layout_height="@dimen/layer_width" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:orientation="horizontal" >
+
+ <com.android.test.hwuicompare.MainView
+ android:id="@+id/software_view"
+ android:layout_width="@dimen/layer_width"
+ android:layout_height="@dimen/layer_width" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/compare_image_view"
+ android:layout_width="@dimen/layer_width_double"
+ android:layout_height="@dimen/layer_height_double"
+ android:filter="false" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal" >
+
+ <ImageButton
+ android:id="@+id/previous"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/previous_combination"
+ android:src="@android:drawable/ic_media_previous" />
+
+ <ImageButton
+ android:id="@+id/next"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/next_combination"
+ android:src="@android:drawable/ic_media_next" />
+
+ <TextView
+ android:id="@+id/current_error"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <Button
+ android:id="@+id/show_hardware_version"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/show_hardware_version" />
+
+ <Button
+ android:id="@+id/show_software_version"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/show_software_version" />
+
+ <Button
+ android:id="@+id/show_error_heatmap"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/show_error_heatmap" />
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/CanvasCompare/res/values/strings.xml b/tests/CanvasCompare/res/values/strings.xml
new file mode 100644
index 0000000..edd4610
--- /dev/null
+++ b/tests/CanvasCompare/res/values/strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="app_name">Canvas Compare Test</string>
+
+ <!-- show hardware rendered version of the layer -->
+ <string name="show_hardware_version">Hardware</string>
+ <!-- show software rendered version of the layer -->
+ <string name="show_software_version">Software</string>
+ <!-- show layer error -->
+ <string name="show_error_values">Error</string>
+ <!-- show layer error heatmap -->
+ <string name="show_error_heatmap">Heatmap</string>
+ <!-- select and display the next combination of painting options-->
+ <string name="next_combination">Next Combination</string>
+ <!-- select and display the previous combination of painting options-->
+ <string name="previous_combination">Previous Combination</string>
+</resources>
diff --git a/tests/CanvasCompare/res/values/values.xml b/tests/CanvasCompare/res/values/values.xml
new file mode 100644
index 0000000..f69378d
--- /dev/null
+++ b/tests/CanvasCompare/res/values/values.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+
+ <!-- NOTE: the below MUST be multiples of 64 -->
+ <dimen name="layer_height">320px</dimen>
+ <dimen name="layer_width">320px</dimen>
+
+ <dimen name="layer_height_double">640px</dimen>
+ <dimen name="layer_width_double">640px</dimen>
+
+</resources>
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
new file mode 100644
index 0000000..e0ff1dc
--- /dev/null
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwuicompare;
+
+import java.util.ArrayList;
+
+import com.android.test.hwuicompare.R;
+
+import android.os.Bundle;
+import android.os.Trace;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+public class AutomaticActivity extends CompareActivity {
+ private static final String LOG_TAG = "AutomaticActivity";
+ private static final float ERROR_DISPLAY_THRESHOLD = 0.01f;
+ protected static final boolean DRAW_BITMAPS = false;
+
+ private ImageView mSoftwareImageView = null;
+ private ImageView mHardwareImageView = null;
+
+ private static final float[] ERROR_CUTOFFS = {0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f};
+ private float[] mErrorRates = new float[ERROR_CUTOFFS.length];
+ private float mTotalTests = 0;
+ private float mTotalError = 0;
+
+ public abstract static class TestCallback {
+ abstract void report(String name, float value);
+ void complete() {}
+ }
+
+ private ArrayList<TestCallback> mTestCallbacks = new ArrayList<TestCallback>();
+
+ Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ loadBitmaps();
+ if (mSoftwareBitmap == null || mHardwareBitmap == null) {
+ Log.e(LOG_TAG, "bitmap is null");
+ return;
+ }
+
+ if (DRAW_BITMAPS) {
+ mSoftwareImageView.setImageBitmap(mSoftwareBitmap);
+ mHardwareImageView.setImageBitmap(mHardwareBitmap);
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "calculateError");
+ float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap);
+ Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+
+ if (error > ERROR_DISPLAY_THRESHOLD) {
+ String modname = "";
+ for (String s : DisplayModifier.getLastAppliedModifications()) {
+ modname = modname.concat(s + ".");
+ }
+ Log.d(LOG_TAG, String.format("error for %s was %2.9f", modname, error));
+ }
+ for (int i = 0; i < ERROR_CUTOFFS.length; i++) {
+ if (error <= ERROR_CUTOFFS[i]) break;
+ mErrorRates[i]++;
+ }
+ mTotalError += error;
+ mTotalTests++;
+
+ if (DisplayModifier.step()) {
+ for (TestCallback c : mTestCallbacks) {
+ c.report("averageError", (mTotalError / mTotalTests));
+ for (int i = 1; i < ERROR_CUTOFFS.length; i++) {
+ c.report(String.format("error over %1.3f", ERROR_CUTOFFS[i]),
+ mErrorRates[i]/mTotalTests);
+ }
+ c.complete();
+ }
+
+ Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show();
+ finish();
+ } else {
+ mHardwareView.invalidate();
+ if (DRAW_BITMAPS) {
+ mSoftwareImageView.invalidate();
+ mHardwareImageView.invalidate();
+ }
+ }
+ mHandler.removeCallbacks(mRunnable);
+ }
+ };
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mHandler.removeCallbacks(mRunnable);
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.automatic_layout);
+
+ mSoftwareImageView = (ImageView) findViewById(R.id.software_image_view);
+ mHardwareImageView = (ImageView) findViewById(R.id.hardware_image_view);
+
+ onCreateCommon(mRunnable);
+ mTestCallbacks.add(new TestCallback() {
+ void report(String name, float value) {
+ Log.d(LOG_TAG, name + " " + value);
+ };
+ });
+ }
+
+ @Override
+ protected boolean forceRecreateBitmaps() {
+ // disable, unless needed for drawing into imageviews
+ return DRAW_BITMAPS;
+ }
+
+ // FOR TESTING
+ public void setCallback(TestCallback c) {
+ mTestCallbacks.add(c);
+ }
+}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java
new file mode 100644
index 0000000..0b85189
--- /dev/null
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwuicompare;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import com.android.test.hwuicompare.R;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Handler;
+import android.os.Trace;
+import android.util.Log;
+import android.view.View;
+
+abstract public class CompareActivity extends Activity {
+ private static final String LOG_TAG = "CompareActivity";
+
+ protected MainView mHardwareView = null;
+
+ protected Bitmap mSoftwareBitmap;
+ protected Bitmap mHardwareBitmap;
+
+ protected ErrorCalculator mErrorCalculator;
+
+ protected Handler mHandler;
+
+ Runnable mDrawCallback = null;
+ protected boolean mRedrewFlag = true;
+
+ protected void onCreateCommon(final Runnable postDrawCallback) {
+ mDrawCallback = new Runnable() {
+ @Override
+ public void run() {
+ mRedrewFlag = true;
+ mHandler.post(postDrawCallback);
+ };
+ };
+ getWindow().setBackgroundDrawable(new ColorDrawable(0xffefefef));
+ ResourceModifiers.init(getResources());
+
+ mHardwareView = (MainView) findViewById(R.id.hardware_view);
+ mHardwareView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mHardwareView.setBackgroundColor(Color.WHITE);
+ mHardwareView.addDrawCallback(mDrawCallback);
+
+ int width = getResources().getDimensionPixelSize(R.dimen.layer_width);
+ int height = getResources().getDimensionPixelSize(R.dimen.layer_height);
+ mSoftwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ mHardwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+ mErrorCalculator = new ErrorCalculator(getApplicationContext(), getResources());
+
+ mHandler = new Handler();
+ }
+
+ protected abstract boolean forceRecreateBitmaps();
+
+ protected void loadBitmaps() {
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "loadBitmaps");
+ if (forceRecreateBitmaps()) {
+ int width = mSoftwareBitmap.getWidth();
+ int height = mSoftwareBitmap.getHeight();
+
+ mSoftwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ mHardwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "softwareDraw");
+ mHardwareView.draw(new Canvas(mSoftwareBitmap));
+ Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+
+ try {
+ Method getHardwareLayer = View.class.getDeclaredMethod("getHardwareLayer");
+ if (!getHardwareLayer.isAccessible())
+ getHardwareLayer.setAccessible(true);
+ Object hardwareLayer = getHardwareLayer.invoke(mHardwareView);
+ if (hardwareLayer == null) {
+ Log.d(LOG_TAG, "failure to access hardware layer");
+ return;
+ }
+ Method copyInto = hardwareLayer.getClass().getSuperclass()
+ .getDeclaredMethod("copyInto", Bitmap.class);
+ if (!copyInto.isAccessible())
+ copyInto.setAccessible(true);
+
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "copyInto");
+ boolean success = (Boolean) copyInto.invoke(hardwareLayer, mHardwareBitmap);
+ Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+ if (!success) {
+ Log.d(LOG_TAG, "failure to copy hardware layer into bitmap");
+ }
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+ }
+}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
new file mode 100644
index 0000000..6022141
--- /dev/null
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwuicompare;
+
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.Log;
+
+public abstract class DisplayModifier {
+
+ // automated tests ignore any combination of operations that don't together return TOTAL_MASK
+ protected final static int TOTAL_MASK = 0x1F;
+
+ // if we're filling, ensure we're not also sweeping over stroke parameters
+ protected final static int SWEEP_STROKE_WIDTH_BIT = 0x1 << 0;
+ protected final static int SWEEP_STROKE_CAP_BIT = 0x1 << 1;
+ protected final static int SWEEP_STROKE_JOIN_BIT = 0x1 << 2;
+
+ protected final static int SWEEP_SHADER_BIT = 0x1 << 3; // only allow non-simple shaders to use rectangle drawing
+ protected final static int SWEEP_TRANSFORM_BIT = 0x1 << 4; // only sweep over specified transforms
+
+ abstract public void modifyDrawing(Paint paint, Canvas canvas);
+ protected int mask() { return 0x0; };
+
+ private static final RectF gRect = new RectF(0, 0, 200, 175);
+ private static final float[] gLinePts = new float[] {
+ 100, 0, 200, 200, 200, 200, 0, 200, 0, 200, 100, 0
+ };
+ private static final float[] gPts = new float[] {
+ 0, 100, 100, 0, 100, 200, 200, 100
+ };
+
+ @SuppressWarnings("serial")
+ private static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> gMaps = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>() {
+ {
+ put("aa", new LinkedHashMap<String, DisplayModifier>() {
+ {
+ put("true", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setAntiAlias(true);
+ }
+ });
+ put("false", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setAntiAlias(false);
+ }
+ });
+ }
+ });
+ put("style", new LinkedHashMap<String, DisplayModifier>() {
+ {
+ put("fill", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStyle(Paint.Style.FILL);
+ }
+ });
+ put("stroke", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStyle(Paint.Style.STROKE);
+ }
+ @Override
+ protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
+ });
+ put("fillAndStroke", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStyle(Paint.Style.FILL_AND_STROKE);
+ }
+
+ @Override
+ protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
+ });
+ }
+ });
+ put("strokeWidth", new LinkedHashMap<String, DisplayModifier>() {
+ {
+ put("hair", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeWidth(0);
+ }
+ @Override
+ protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
+ });
+ put("0.3", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeWidth(0.3f);
+ }
+ });
+ put("1", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeWidth(1);
+ }
+ });
+ put("5", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeWidth(5);
+ }
+ });
+ }
+ });
+ put("strokeCap", new LinkedHashMap<String, DisplayModifier>() {
+ {
+ put("butt", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeCap(Paint.Cap.BUTT);
+ }
+ @Override
+ protected int mask() { return SWEEP_STROKE_CAP_BIT; }
+ });
+ put("round", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeCap(Paint.Cap.ROUND);
+ }
+ });
+ put("square", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeCap(Paint.Cap.SQUARE);
+ }
+ });
+ }
+ });
+ put("strokeJoin", new LinkedHashMap<String, DisplayModifier>() {
+ {
+ put("bevel", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeJoin(Paint.Join.BEVEL);
+ }
+ @Override
+ protected int mask() { return SWEEP_STROKE_JOIN_BIT; }
+ });
+ put("round", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeJoin(Paint.Join.ROUND);
+ }
+ });
+ put("miter", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setStrokeJoin(Paint.Join.MITER);
+ }
+ });
+ // TODO: add miter0, miter1 etc to test miter distances
+ }
+ });
+
+ put("transform", new LinkedHashMap<String, DisplayModifier>() {
+ {
+ put("noTransform", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {}
+ @Override
+ protected int mask() { return SWEEP_TRANSFORM_BIT; };
+ });
+ put("rotate5", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.rotate(5);
+ }
+ });
+ put("rotate45", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.rotate(5);
+ }
+ });
+ put("rotate90", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.rotate(90);
+ canvas.translate(0, -200);
+ }
+ });
+ put("scale2x2", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.scale(2, 2);
+ }
+ @Override
+ protected int mask() { return SWEEP_TRANSFORM_BIT; };
+ });
+ put("rot20scl1x4", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.rotate(20);
+ canvas.scale(1, 4);
+ }
+ @Override
+ protected int mask() { return SWEEP_TRANSFORM_BIT; };
+ });
+ }
+ });
+
+ put("shader", new LinkedHashMap<String, DisplayModifier>() {
+ {
+ put("noShader", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {}
+ @Override
+ protected int mask() { return SWEEP_SHADER_BIT; };
+ });
+ put("repeatShader", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mRepeatShader);
+ }
+ @Override
+ protected int mask() { return SWEEP_SHADER_BIT; };
+ });
+ put("translatedShader", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mTranslatedShader);
+ }
+ });
+ put("scaledShader", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mScaledShader);
+ }
+ });
+ put("horGradient", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mHorGradient);
+ }
+ });
+ put("diagGradient", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mDiagGradient);
+ }
+ @Override
+ protected int mask() { return SWEEP_SHADER_BIT; };
+ });
+ put("vertGradient", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mVertGradient);
+ }
+ });
+ }
+ });
+
+ // FINAL MAP: DOES ACTUAL DRAWING
+ put("drawing", new LinkedHashMap<String, DisplayModifier>() {
+ {
+ put("roundRect", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.drawRoundRect(gRect, 20, 20, paint);
+ }
+ });
+ put("rect", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.drawRect(gRect, paint);
+ }
+ @Override
+ protected int mask() { return SWEEP_SHADER_BIT | SWEEP_STROKE_CAP_BIT; };
+ });
+ put("circle", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.drawCircle(100, 100, 75, paint);
+ }
+ });
+ put("oval", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.drawOval(gRect, paint);
+ }
+ });
+ put("triLines", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.drawLines(gLinePts, paint);
+ }
+ @Override
+ protected int mask() { return SWEEP_STROKE_CAP_BIT; };
+ });
+ put("plusPoints", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.drawPoints(gPts, paint);
+ }
+ });
+ put("text", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setTextSize(36);
+ canvas.drawText("TEXTTEST", 0, 50, paint);
+ }
+ });
+ put("shadowtext", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setTextSize(36);
+ paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
+ canvas.drawText("TEXTTEST", 0, 50, paint);
+ }
+ });
+ put("bitmapMesh", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.drawBitmapMesh(ResourceModifiers.instance().mBitmap, 3, 3,
+ ResourceModifiers.instance().mBitmapVertices, 0, null, 0, null);
+ }
+ });
+ put("arc", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.drawArc(gRect, 260, 285, false, paint);
+ }
+ @Override
+ protected int mask() { return SWEEP_STROKE_CAP_BIT; };
+ });
+ put("arcFromCenter", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ canvas.drawArc(gRect, 260, 285, true, paint);
+ }
+ @Override
+ protected int mask() { return SWEEP_STROKE_JOIN_BIT; };
+ });
+ }
+ });
+ // WARNING: DON'T PUT MORE MAPS BELOW THIS
+ }
+ };
+
+ private static LinkedHashMap<String, DisplayModifier> getMapAtIndex(int index) {
+ for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
+ if (index == 0) {
+ return map;
+ }
+ index--;
+ }
+ return null;
+ }
+
+ // indices instead of iterators for easier bidirectional traversal
+ private static final int mIndices[] = new int[gMaps.size()];
+ private static final String[] mLastAppliedModifications = new String[gMaps.size()];
+
+ private static boolean stepInternal(boolean forward) {
+ int modifierMapIndex = gMaps.size() - 1;
+ while (modifierMapIndex >= 0) {
+ LinkedHashMap<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex);
+ mIndices[modifierMapIndex] += (forward ? 1 : -1);
+
+ if (mIndices[modifierMapIndex] >= 0 && mIndices[modifierMapIndex] < map.size()) {
+ break;
+ }
+
+ mIndices[modifierMapIndex] = (forward ? 0 : map.size() - 1);
+ modifierMapIndex--;
+ }
+ return modifierMapIndex < 0; // true if resetting
+ }
+
+ public static boolean step() {
+ boolean ret = false;
+ do {
+ ret |= stepInternal(true);
+ } while (!checkModificationStateMask());
+ return ret;
+ }
+
+ public static boolean stepBack() {
+ boolean ret = false;
+ do {
+ ret |= stepInternal(false);
+ } while (!checkModificationStateMask());
+ return ret;
+ }
+
+ private static boolean checkModificationStateMask() {
+ int operatorMask = 0x0;
+ int mapIndex = 0;
+ for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
+ int displayModifierIndex = mIndices[mapIndex];
+ for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
+ if (displayModifierIndex == 0) {
+ mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
+ operatorMask |= modifierEntry.getValue().mask();
+ break;
+ }
+ displayModifierIndex--;
+ }
+ mapIndex++;
+ }
+ return operatorMask == TOTAL_MASK;
+ }
+
+ public static void apply(Paint paint, Canvas canvas) {
+ int mapIndex = 0;
+ for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
+ int displayModifierIndex = mIndices[mapIndex];
+ for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
+ if (displayModifierIndex == 0) {
+ mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
+ modifierEntry.getValue().modifyDrawing(paint, canvas);
+ break;
+ }
+ displayModifierIndex--;
+ }
+ mapIndex++;
+ }
+ }
+
+ public static String[] getLastAppliedModifications() {
+ return mLastAppliedModifications.clone();
+ }
+
+ public static String[][] getStrings() {
+ String[][] keys = new String[gMaps.size()][];
+
+ int i = 0;
+ for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
+ keys[i] = new String[map.size()];
+ int j = 0;
+ for (String key : map.keySet()) {
+ keys[i][j++] = key;
+ }
+ i++;
+ }
+
+ return keys;
+ }
+
+ public static void setIndex(int mapIndex, int newIndexValue) {
+ mIndices[mapIndex] = newIndexValue;
+ }
+
+ public static int[] getIndices() {
+ return mIndices;
+ }
+}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java
new file mode 100644
index 0000000..a08b558
--- /dev/null
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwuicompare;
+
+import com.android.test.hwuicompare.R;
+import com.android.test.hwuicompare.ScriptC_errorCalculator;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+public class ErrorCalculator {
+ private static final String LOG_TAG = "ErrorCalculator";
+ private static final int REGION_SIZE = 8;
+
+ private static final boolean LOG_TIMING = false;
+ private static final boolean LOG_CALC = false;
+
+ private final RenderScript mRS;
+ private Allocation mIdealPixelsAllocation;
+ private Allocation mGivenPixelsAllocation;
+ private Allocation mOutputPixelsAllocation;
+
+ private final Allocation mInputRowsAllocation;
+ private final Allocation mOutputRegionsAllocation;
+
+ private final ScriptC_errorCalculator mScript;
+
+ private final int[] mOutputRowRegions;
+
+ public ErrorCalculator(Context c, Resources resources) {
+ int width = resources.getDimensionPixelSize(R.dimen.layer_width);
+ int height = resources.getDimensionPixelSize(R.dimen.layer_height);
+ mOutputRowRegions = new int[height / REGION_SIZE];
+
+ mRS = RenderScript.create(c);
+ int[] rowIndices = new int[height / REGION_SIZE];
+ for (int i = 0; i < rowIndices.length; i++)
+ rowIndices[i] = i * REGION_SIZE;
+
+ mScript = new ScriptC_errorCalculator(mRS, resources, R.raw.errorcalculator);
+ mScript.set_HEIGHT(height);
+ mScript.set_WIDTH(width);
+ mScript.set_REGION_SIZE(REGION_SIZE);
+
+ mInputRowsAllocation = Allocation.createSized(mRS, Element.I32(mRS), rowIndices.length,
+ Allocation.USAGE_SCRIPT);
+ mInputRowsAllocation.copyFrom(rowIndices);
+ mOutputRegionsAllocation = Allocation.createSized(mRS, Element.I32(mRS),
+ mOutputRowRegions.length, Allocation.USAGE_SCRIPT);
+ }
+
+
+ private static long startMillis, middleMillis;
+
+ public float calcErrorRS(Bitmap ideal, Bitmap given) {
+ if (LOG_TIMING) {
+ startMillis = System.currentTimeMillis();
+ }
+
+ mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal,
+ Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+ mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given,
+ Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+
+ mScript.bind_ideal(mIdealPixelsAllocation);
+ mScript.bind_given(mGivenPixelsAllocation);
+
+ mScript.forEach_countInterestingRegions(mInputRowsAllocation, mOutputRegionsAllocation);
+ mOutputRegionsAllocation.copyTo(mOutputRowRegions);
+
+ int regionCount = 0;
+ for (int region : mOutputRowRegions) {
+ regionCount += region;
+ }
+ int interestingPixels = Math.max(1, regionCount) * REGION_SIZE * REGION_SIZE;
+
+ if (LOG_TIMING) {
+ long startMillis2 = System.currentTimeMillis();
+ }
+
+ mScript.forEach_accumulateError(mInputRowsAllocation, mOutputRegionsAllocation);
+ mOutputRegionsAllocation.copyTo(mOutputRowRegions);
+ float totalError = 0;
+ for (int row : mOutputRowRegions) {
+ totalError += row;
+ }
+ totalError /= 1024.0f;
+
+ if (LOG_TIMING) {
+ long finalMillis = System.currentTimeMillis();
+ Log.d(LOG_TAG, "rs: first part took " + (middleMillis - startMillis) + "ms");
+ Log.d(LOG_TAG, "rs: last part took " + (finalMillis - middleMillis) + "ms");
+ }
+ if (LOG_CALC) {
+ Log.d(LOG_TAG, "rs: error " + totalError + ", pixels " + interestingPixels);
+ }
+ return totalError / interestingPixels;
+ }
+
+ public void calcErrorHeatmapRS(Bitmap ideal, Bitmap given, Bitmap output) {
+ mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal,
+ Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+ mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given,
+ Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+
+ mScript.bind_ideal(mIdealPixelsAllocation);
+ mScript.bind_given(mGivenPixelsAllocation);
+
+ mOutputPixelsAllocation = Allocation.createFromBitmap(mRS, output,
+ Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+ mScript.forEach_displayDifference(mOutputPixelsAllocation, mOutputPixelsAllocation);
+ mOutputPixelsAllocation.copyTo(output);
+ }
+
+ public static float calcError(Bitmap ideal, Bitmap given) {
+ if (LOG_TIMING) {
+ startMillis = System.currentTimeMillis();
+ }
+
+ int interestingRegions = 0;
+ for (int x = 0; x < ideal.getWidth(); x += REGION_SIZE) {
+ for (int y = 0; y < ideal.getWidth(); y += REGION_SIZE) {
+ if (inspectRegion(ideal, x, y)) {
+ interestingRegions++;
+ }
+ }
+ }
+
+ int interestingPixels = Math.max(1, interestingRegions) * REGION_SIZE * REGION_SIZE;
+
+ if (LOG_TIMING) {
+ long startMillis2 = System.currentTimeMillis();
+ }
+
+ float totalError = 0;
+ for (int x = 0; x < ideal.getWidth(); x++) {
+ for (int y = 0; y < ideal.getHeight(); y++) {
+ int idealColor = ideal.getPixel(x, y);
+ int givenColor = given.getPixel(x, y);
+ if (idealColor == givenColor)
+ continue;
+ totalError += Math.abs(Color.red(idealColor) - Color.red(givenColor));
+ totalError += Math.abs(Color.green(idealColor) - Color.green(givenColor));
+ totalError += Math.abs(Color.blue(idealColor) - Color.blue(givenColor));
+ totalError += Math.abs(Color.alpha(idealColor) - Color.alpha(givenColor));
+ }
+ }
+ totalError /= 1024.0f;
+ if (LOG_TIMING) {
+ long finalMillis = System.currentTimeMillis();
+ Log.d(LOG_TAG, "dvk: first part took " + (middleMillis - startMillis) + "ms");
+ Log.d(LOG_TAG, "dvk: last part took " + (finalMillis - middleMillis) + "ms");
+ }
+ if (LOG_CALC) {
+ Log.d(LOG_TAG, "dvk: error " + totalError + ", pixels " + interestingPixels);
+ }
+ return totalError / interestingPixels;
+ }
+
+ private static boolean inspectRegion(Bitmap ideal, int x, int y) {
+ int regionColor = ideal.getPixel(x, y);
+ for (int i = 0; i < REGION_SIZE; i++) {
+ for (int j = 0; j < REGION_SIZE; j++) {
+ if (ideal.getPixel(x + i, y + j) != regionColor)
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java
new file mode 100644
index 0000000..454fe7b
--- /dev/null
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwuicompare;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class MainView extends View {
+ Paint mPaint = new Paint();
+
+ public MainView(Context context) {
+ super(context);
+ }
+
+ public MainView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public MainView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ mPaint.reset();
+ DisplayModifier.apply(mPaint, canvas);
+
+ if (mDrawCallback != null) {
+ mDrawCallback.run();
+ }
+ }
+
+ private Runnable mDrawCallback;
+ public void addDrawCallback(Runnable drawCallback) {
+ mDrawCallback = drawCallback;
+ }
+}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
new file mode 100644
index 0000000..400dff0
--- /dev/null
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwuicompare;
+
+import com.android.test.hwuicompare.R;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+public class ManualActivity extends CompareActivity {
+ private static final String LOG_TAG = "ManualActivity";
+ private ImageView mCompareImageView;
+ private Bitmap mCompareBitmap;
+ private TextView mErrorTextView;
+ private MainView mSoftwareView;
+
+ private static final int COMPARE_VIEW_UNINITIALIZED = -1;
+ private static final int COMPARE_VIEW_HARDWARE = 0;
+ private static final int COMPARE_VIEW_SOFTWARE = 1;
+ private static final int COMPARE_VIEW_HEATMAP = 2; // TODO: add more like this? any ideas?
+
+ private int mCompareImageViewState = COMPARE_VIEW_UNINITIALIZED;
+ private int mLastCompareImageViewState = COMPARE_VIEW_UNINITIALIZED;
+
+ Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ Log.d(LOG_TAG, "mRunnable running, mRedrewFlag = " + mRedrewFlag);
+
+ if (mRedrewFlag) {
+ loadBitmaps();
+ // recalculate error
+ float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap);
+ String modname = "";
+ for (String s : DisplayModifier.getLastAppliedModifications()) {
+ modname = modname.concat(s + ".");
+ }
+
+ Log.d(LOG_TAG, "error for " + modname + " is " + error);
+ mErrorTextView.setText(String.format("%.4f", error));
+ }
+
+ if (mCompareImageViewState != mLastCompareImageViewState || mRedrewFlag) {
+ switch (mCompareImageViewState) {
+ case COMPARE_VIEW_UNINITIALIZED:
+ // set to hardware
+ case COMPARE_VIEW_HARDWARE:
+ mCompareImageView.setImageBitmap(mHardwareBitmap);
+ break;
+ case COMPARE_VIEW_SOFTWARE:
+ mCompareImageView.setImageBitmap(mSoftwareBitmap);
+ break;
+ case COMPARE_VIEW_HEATMAP:
+ mErrorCalculator.calcErrorHeatmapRS(mSoftwareBitmap, mHardwareBitmap,
+ mCompareBitmap);
+ mCompareImageView.setImageBitmap(mCompareBitmap);
+ break;
+ }
+ mCompareImageView.invalidate();
+ }
+
+ mLastCompareImageViewState = mCompareImageViewState;
+ mRedrewFlag = false;
+ mHandler.removeCallbacks(mRunnable);
+ }
+ };
+
+ private void redrawViews() {
+ mHardwareView.invalidate();
+ mSoftwareView.invalidate();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.manual_layout);
+ onCreateCommon(mRunnable);
+
+ mSoftwareView = (MainView) findViewById(R.id.software_view);
+ mSoftwareView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ mSoftwareView.setBackgroundColor(Color.WHITE);
+ mSoftwareView.addDrawCallback(mDrawCallback);
+
+ mCompareImageView = (ImageView) findViewById(R.id.compare_image_view);
+
+ int width = getResources().getDimensionPixelSize(R.dimen.layer_width);
+ int height = getResources().getDimensionPixelSize(R.dimen.layer_height);
+ mCompareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+ mErrorTextView = (TextView) findViewById(R.id.current_error);
+ ((ImageButton) findViewById(R.id.next)).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ DisplayModifier.step();
+ updateSpinners();
+ redrawViews();
+ }
+ });
+ ((ImageButton) findViewById(R.id.previous)).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ DisplayModifier.stepBack();
+ updateSpinners();
+ redrawViews();
+ }
+ });
+ ((Button) findViewById(R.id.show_hardware_version))
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCompareImageViewState = COMPARE_VIEW_HARDWARE;
+ mHandler.post(mRunnable);
+ }
+ });
+ ((Button) findViewById(R.id.show_software_version))
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCompareImageViewState = COMPARE_VIEW_SOFTWARE;
+ mHandler.post(mRunnable);
+ }
+ });
+ ((Button) findViewById(R.id.show_error_heatmap)).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCompareImageViewState = COMPARE_VIEW_HEATMAP;
+ mHandler.post(mRunnable);
+ }
+ });
+
+ buildSpinnerLayout();
+ }
+
+ private class DisplayModifierSpinner extends Spinner {
+ private final int mIndex;
+
+ public DisplayModifierSpinner(int index) {
+ super(ManualActivity.this);
+ mIndex = index;
+ setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parentView, View selectedItem,
+ int position, long id) {
+ DisplayModifier.setIndex(mIndex, position);
+ redrawViews();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parentView) {
+ }
+ });
+ }
+ }
+
+ private Spinner[] mSpinners;
+
+ private void buildSpinnerLayout() {
+ LinearLayout layout = (LinearLayout) findViewById(R.id.spinner_layout);
+ String[][] mapsStrings = DisplayModifier.getStrings();
+ mSpinners = new Spinner[mapsStrings.length];
+ int index = 0;
+ for (String[] spinnerValues : mapsStrings) {
+ mSpinners[index] = new DisplayModifierSpinner(index);
+ mSpinners[index].setAdapter(new ArrayAdapter<String>(this,
+ android.R.layout.simple_spinner_dropdown_item, spinnerValues));
+ layout.addView(mSpinners[index]);
+ index++;
+ }
+ Log.d(LOG_TAG, "created " + index + " spinners");
+ }
+
+ private void updateSpinners() {
+ int[] indices = DisplayModifier.getIndices();
+ for (int i = 0; i < mSpinners.length; i++) {
+ mSpinners[i].setSelection(indices[i]);
+ }
+ }
+
+ @Override
+ protected boolean forceRecreateBitmaps() {
+ // continually recreate bitmaps to avoid modifying bitmaps currently being drawn
+ return true;
+ }
+}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
new file mode 100644
index 0000000..c705443
--- /dev/null
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwuicompare;
+
+import com.android.test.hwuicompare.R;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Shader;
+
+public class ResourceModifiers {
+ public final BitmapShader mRepeatShader;
+ public final BitmapShader mTranslatedShader;
+ public final BitmapShader mScaledShader;
+ private final int mTexWidth;
+ private final int mTexHeight;
+ private final float mDrawWidth;
+ private final float mDrawHeight;
+ public final LinearGradient mHorGradient;
+ public final LinearGradient mDiagGradient;
+ public final LinearGradient mVertGradient;
+ public final Bitmap mBitmap;
+ private final Matrix mMtx1;
+ private final Matrix mMtx2;
+ private final Matrix mMtx3;
+
+ public final float[] mBitmapVertices;
+ public final int[] mBitmapColors;
+
+ private static ResourceModifiers sInstance = null;
+ public static ResourceModifiers instance() { return sInstance; }
+ public static void init(Resources resources) {
+ sInstance = new ResourceModifiers(resources);
+ }
+
+ public ResourceModifiers(Resources resources) {
+ mBitmap = BitmapFactory.decodeResource(resources, R.drawable.sunset1);
+ mTexWidth = mBitmap.getWidth();
+ mTexHeight = mBitmap.getHeight();
+
+ mDrawWidth = resources.getDimensionPixelSize(R.dimen.layer_width);
+ mDrawHeight = resources.getDimensionPixelSize(R.dimen.layer_height);
+
+ mRepeatShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT);
+
+ mTranslatedShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT);
+ mMtx1 = new Matrix();
+ mMtx1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
+ mMtx1.postRotate(45, 0, 0);
+ mTranslatedShader.setLocalMatrix(mMtx1);
+
+ mScaledShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ mMtx2 = new Matrix();
+ mMtx2.setScale(0.5f, 0.5f);
+ mScaledShader.setLocalMatrix(mMtx2);
+
+ mHorGradient = new LinearGradient(0.0f, 0.0f, 1.0f, 0.0f,
+ Color.RED, Color.GREEN, Shader.TileMode.CLAMP);
+ mMtx3 = new Matrix();
+ mMtx3.setScale(mDrawHeight, 1.0f);
+ mMtx3.postRotate(-90.0f);
+ mMtx3.postTranslate(0.0f, mDrawHeight);
+ mHorGradient.setLocalMatrix(mMtx3);
+
+ mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 2.0f, mDrawHeight / 2.0f,
+ Color.BLUE, Color.RED, Shader.TileMode.CLAMP);
+
+ mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
+ Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
+
+ final float width = mBitmap.getWidth() / 8.0f;
+ final float height = mBitmap.getHeight() / 8.0f;
+
+ mBitmapVertices = new float[] {
+ 0.0f, 0.0f, width, 0.0f, width * 2, 0.0f, width * 3, 0.0f,
+ 0.0f, height, width, height, width * 2, height, width * 4, height,
+ 0.0f, height * 2, width, height * 2, width * 2, height * 2, width * 3, height * 2,
+ 0.0f, height * 4, width, height * 4, width * 2, height * 4, width * 4, height * 4,
+ };
+
+ mBitmapColors = new int[] {
+ 0xffff0000, 0xff00ff00, 0xff0000ff, 0xffff0000,
+ 0xff0000ff, 0xffff0000, 0xff00ff00, 0xff00ff00,
+ 0xff00ff00, 0xff0000ff, 0xffff0000, 0xff00ff00,
+ 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff0000,
+ };
+ }
+
+}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
new file mode 100644
index 0000000..6ea8237
--- /dev/null
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
@@ -0,0 +1,46 @@
+package com.android.test.hwuicompare;
+
+import com.android.test.hwuicompare.AutomaticActivity.TestCallback;
+
+import android.os.Bundle;
+import android.test.ActivityInstrumentationTestCase2;
+
+public class Test extends ActivityInstrumentationTestCase2<AutomaticActivity> {
+ AutomaticActivity mActivity;
+ private Bundle mBundle;
+
+ public Test() {
+ super(AutomaticActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mBundle = new Bundle();
+ mActivity = getActivity();
+ mActivity.setCallback(new TestCallback() {
+
+ @Override
+ void report(String key, float value) {
+ mBundle.putFloat(key, value);
+ }
+ @Override
+ void complete() {
+ synchronized(mBundle) {
+ mBundle.notify();
+ }
+ }
+ });
+ }
+
+ public void testCanvas() {
+ synchronized(mBundle) {
+ try {
+ mBundle.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ getInstrumentation().sendStatus(0, mBundle);
+ }
+}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs
new file mode 100644
index 0000000..668f61d
--- /dev/null
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs
@@ -0,0 +1,56 @@
+#pragma version(1)
+#pragma rs java_package_name(com.android.test.hwuicompare)
+
+int REGION_SIZE;
+int WIDTH;
+int HEIGHT;
+
+const uchar4 *ideal;
+const uchar4 *given;
+
+void countInterestingRegions(const int32_t *v_in, int32_t *v_out) {
+ int y = v_in[0];
+ v_out[0] = 0;
+
+ for (int x = 0; x < HEIGHT; x += REGION_SIZE) {
+ bool interestingRegion = false;
+ int regionColor = (int)ideal[y * WIDTH + x];
+ for (int i = 0; i < REGION_SIZE && !interestingRegion; i++) {
+ for (int j = 0; j < REGION_SIZE && !interestingRegion; j++) {
+ interestingRegion |= (int)(ideal[(y + i) * WIDTH + (x + j)]) != regionColor;
+ }
+ }
+ if (interestingRegion) {
+ v_out[0]++;
+ }
+ }
+}
+
+void accumulateError(const int32_t *v_in, int32_t *v_out) {
+ int startY = v_in[0];
+ int error = 0;
+ for (int y = startY; y < startY + REGION_SIZE; y++) {
+ for (int x = 0; x < HEIGHT; x++) {
+ uchar4 idealPixel = ideal[y * WIDTH + x];
+ uchar4 givenPixel = given[y * WIDTH + x];
+ error += abs(idealPixel.x - givenPixel.x);
+ error += abs(idealPixel.y - givenPixel.y);
+ error += abs(idealPixel.z - givenPixel.z);
+ error += abs(idealPixel.w - givenPixel.w);
+ }
+ }
+ v_out[0] = error;
+}
+
+void displayDifference(const uchar4 *v_in, uchar4 *v_out, uint32_t x, uint32_t y) {
+ float4 idealPixel = rsUnpackColor8888(ideal[y * WIDTH + x]);
+ float4 givenPixel = rsUnpackColor8888(given[y * WIDTH + x]);
+
+ float4 diff = idealPixel - givenPixel;
+ float totalDiff = diff.x + diff.y + diff.z + diff.w;
+ if (totalDiff < 0) {
+ v_out[0] = rsPackColorTo8888(0, 0, clamp(-totalDiff/2, 0, 1));
+ } else {
+ v_out[0] = rsPackColorTo8888(clamp(totalDiff/2, 0, 1), 0, 0);
+ }
+}