Hardware / Software Canvas comparison tool

Has automated and manual testing modes

Change-Id: I84d27447ad64021540525372022ab13a36ffc116
This commit is contained in:
Chris Craik
2012-10-01 18:22:38 -07:00
parent 74e6489d89
commit c3683b552f
16 changed files with 1685 additions and 0 deletions

View File

@@ -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)

View File

@@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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,
};
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}