diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/rs/java/ModelViewer/AndroidManifest.xml | 11 | ||||
-rw-r--r-- | libs/rs/java/ModelViewer/res/menu/loader_menu.xml | 25 | ||||
-rw-r--r-- | libs/rs/java/ModelViewer/res/values/strings.xml | 24 | ||||
-rw-r--r-- | libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java | 110 | ||||
-rw-r--r-- | libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java | 43 | ||||
-rw-r--r-- | libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java | 97 | ||||
-rw-r--r-- | libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java | 130 | ||||
-rw-r--r-- | libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs | 124 | ||||
-rw-r--r-- | libs/ui/Android.mk | 7 | ||||
-rw-r--r-- | libs/ui/Input.cpp | 297 | ||||
-rw-r--r-- | libs/ui/tests/InputPublisherAndConsumer_test.cpp | 78 |
11 files changed, 805 insertions, 141 deletions
diff --git a/libs/rs/java/ModelViewer/AndroidManifest.xml b/libs/rs/java/ModelViewer/AndroidManifest.xml index 959fe53..e5e449b 100644 --- a/libs/rs/java/ModelViewer/AndroidManifest.xml +++ b/libs/rs/java/ModelViewer/AndroidManifest.xml @@ -3,14 +3,19 @@ package="com.android.modelviewer"> <application android:label="ModelViewer"> <activity android:name="SimpleModel" - android:label="SimpleModel" - android:screenOrientation="portrait" - android:theme="@android:style/Theme.Black.NoTitleBar"> + android:label="SimpleModel"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity android:name="A3DSelector" + android:label="A3DSelector" + android:hardwareAccelerated="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> <activity android:name="SceneGraph" android:label="SceneGraph" android:theme="@android:style/Theme.Black.NoTitleBar"> diff --git a/libs/rs/java/ModelViewer/res/menu/loader_menu.xml b/libs/rs/java/ModelViewer/res/menu/loader_menu.xml new file mode 100644 index 0000000..3c34023 --- /dev/null +++ b/libs/rs/java/ModelViewer/res/menu/loader_menu.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +* Copyright (C) 2011 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. +*/ +--> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/load_model" + android:title="@string/load_model" /> + <item android:id="@+id/display_options" + android:title="@string/display_options" /> +</menu> diff --git a/libs/rs/java/ModelViewer/res/values/strings.xml b/libs/rs/java/ModelViewer/res/values/strings.xml new file mode 100644 index 0000000..8e1673f --- /dev/null +++ b/libs/rs/java/ModelViewer/res/values/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +* Copyright (C) 2011 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <skip /> + <string name="load_model">Load Model</string> + <string name="display_options">Display Options</string> +</resources> diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java new file mode 100644 index 0000000..0e2004f --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2011 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.modelviewer; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.List; + +import android.app.ListActivity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +/** + * A list view where the last item the user clicked is placed in + * the "activated" state, causing its background to highlight. + */ +public class A3DSelector extends ListActivity { + + File[] mCurrentSubList; + File mCurrentFile; + + class A3DFilter implements FileFilter { + public boolean accept(File file) { + if (file.isDirectory()) { + return true; + } + return file.getName().endsWith(".a3d"); + } + } + + private void populateList(File file) { + + mCurrentFile = file; + setTitle(mCurrentFile.getAbsolutePath() + "/*.a3d"); + List<String> names = new ArrayList<String>(); + names.add(".."); + + mCurrentSubList = mCurrentFile.listFiles(new A3DFilter()); + + if (mCurrentSubList != null) { + for (int i = 0; i < mCurrentSubList.length; i ++) { + String fileName = mCurrentSubList[i].getName(); + if (mCurrentSubList[i].isDirectory()) { + fileName = "/" + fileName; + } + names.add(fileName); + } + } + + // Use the built-in layout for showing a list item with a single + // line of text whose background is changes when activated. + setListAdapter(new ArrayAdapter<String>(this, + android.R.layout.simple_list_item_activated_1, names)); + getListView().setTextFilterEnabled(true); + + // Tell the list view to show one checked/activated item at a time. + getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + populateList(new File("/sdcard/")); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + if (position == 0) { + File parent = mCurrentFile.getParentFile(); + if (parent == null) { + return; + } + populateList(parent); + return; + } + + // the first thing in list is parent directory + File selectedFile = mCurrentSubList[position - 1]; + if (selectedFile.isDirectory()) { + populateList(selectedFile); + return; + } + + Intent resultIntent = new Intent(); + resultIntent.setData(Uri.fromFile(selectedFile)); + setResult(RESULT_OK, resultIntent); + finish(); + } + +} diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java index 9910970..01cb709 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java @@ -21,6 +21,7 @@ import android.renderscript.RenderScript; import android.app.Activity; import android.content.res.Configuration; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -31,9 +32,11 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.MenuInflater; import android.view.Window; import android.widget.Button; import android.widget.ListView; +import android.net.Uri; import java.lang.Runtime; @@ -67,5 +70,45 @@ public class SimpleModel extends Activity { mView.pause(); } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.loader_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection + switch (item.getItemId()) { + case R.id.load_model: + loadModel(); + return true; + case R.id.display_options: + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private static final int FIND_A3D_MODEL = 10; + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + if (requestCode == FIND_A3D_MODEL) { + Uri selectedImageUri = data.getData(); + Log.e("Selected Path: ", selectedImageUri.getPath()); + mView.loadA3DFile(selectedImageUri.getPath()); + } + } + } + + public void loadModel() { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_PICK); + intent.setClassName("com.android.modelviewer", + "com.android.modelviewer.A3DSelector"); + startActivityForResult(intent, FIND_A3D_MODEL); + } + } diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java index b18a327..63ef466 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 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. @@ -26,24 +26,20 @@ import android.util.Log; public class SimpleModelRS { - private final int STATE_LAST_FOCUS = 1; - - int mWidth; - int mHeight; - int mRotation; - public SimpleModelRS() { } - public void init(RenderScriptGL rs, Resources res, int width, int height) { + public void init(RenderScriptGL rs, Resources res) { mRS = rs; mRes = res; - mWidth = width; - mHeight = height; - mRotation = 0; initRS(); } + public void surfaceChanged() { + mRS.getWidth(); + mRS.getHeight(); + } + private Resources mRes; private RenderScriptGL mRS; private Sampler mSampler; @@ -55,34 +51,23 @@ public class SimpleModelRS { private Allocation mGridImage; private Allocation mAllocPV; - private Mesh mMesh; - private Font mItalic; private Allocation mTextAlloc; + private ScriptField_MeshInfo mMeshes; private ScriptC_simplemodel mScript; - int mLastX; - int mLastY; - public void touchEvent(int x, int y) { - int dx = mLastX - x; - if (Math.abs(dx) > 50 || Math.abs(dx) < 3) { - dx = 0; - } - - mRotation -= dx; - if (mRotation > 360) { - mRotation -= 360; - } - if (mRotation < 0) { - mRotation += 360; - } + public void onActionDown(float x, float y) { + mScript.invoke_onActionDown(x, y); + } - mScript.set_gRotate((float)mRotation); + public void onActionScale(float scale) { + mScript.invoke_onActionScale(scale); + } - mLastX = x; - mLastY = y; + public void onActionMove(float x, float y) { + mScript.invoke_onActionMove(x, y); } private void initPFS() { @@ -130,12 +115,48 @@ public class SimpleModelRS { mScript.set_gTGrid(mGridImage); } - private void initTextAllocation() { - String allocString = "Displaying file: R.raw.robot"; + private void initTextAllocation(String fileName) { + String allocString = "Displaying file: " + fileName; mTextAlloc = Allocation.createFromString(mRS, allocString, Allocation.USAGE_SCRIPT); mScript.set_gTextAlloc(mTextAlloc); } + private void initMeshes(FileA3D model) { + int numEntries = model.getIndexEntryCount(); + int numMeshes = 0; + for (int i = 0; i < numEntries; i ++) { + FileA3D.IndexEntry entry = model.getIndexEntry(i); + if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) { + numMeshes ++; + } + } + + if (numMeshes > 0) { + mMeshes = new ScriptField_MeshInfo(mRS, numMeshes); + + for (int i = 0; i < numEntries; i ++) { + FileA3D.IndexEntry entry = model.getIndexEntry(i); + if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) { + Mesh mesh = entry.getMesh(); + mMeshes.set_mMesh(i, mesh, false); + mMeshes.set_mNumIndexSets(i, mesh.getPrimitiveCount(), false); + } + } + mMeshes.copyAll(); + } else { + throw new RSRuntimeException("No valid meshes in file"); + } + + mScript.bind_gMeshes(mMeshes); + mScript.invoke_updateMeshInfo(); + } + + public void loadA3DFile(String path) { + FileA3D model = FileA3D.createFromFile(mRS, path); + initMeshes(model); + initTextAllocation(path); + } + private void initRS() { mScript = new ScriptC_simplemodel(mRS, mRes, R.raw.simplemodel); @@ -147,18 +168,12 @@ public class SimpleModelRS { loadImage(); FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot); - FileA3D.IndexEntry entry = model.getIndexEntry(0); - if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) { - Log.e("rs", "could not load model"); - } else { - mMesh = (Mesh)entry.getObject(); - mScript.set_gTestMesh(mMesh); - } + initMeshes(model); mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8); mScript.set_gItalic(mItalic); - initTextAllocation(); + initTextAllocation("R.raw.robot"); mRS.bindRootScript(mScript); } diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java index 5c5956d..9ab0f22 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 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. @@ -16,81 +16,129 @@ package com.android.modelviewer; -import java.io.Writer; -import java.util.ArrayList; -import java.util.concurrent.Semaphore; - import android.renderscript.RSSurfaceView; -import android.renderscript.RenderScript; import android.renderscript.RenderScriptGL; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.Message; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.ScaleGestureDetector; +import android.util.Log; public class SimpleModelView extends RSSurfaceView { - public SimpleModelView(Context context) { - super(context); - //setFocusable(true); - } - private RenderScriptGL mRS; private SimpleModelRS mRender; + private ScaleGestureDetector mScaleDetector; - public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { - super.surfaceChanged(holder, format, w, h); + private static final int INVALID_POINTER_ID = -1; + private int mActivePointerId = INVALID_POINTER_ID; + + public SimpleModelView(Context context) { + super(context); + ensureRenderScript(); + mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); + } + + private void ensureRenderScript() { if (mRS == null) { RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); sc.setDepth(16, 24); mRS = createRenderScriptGL(sc); - mRS.setSurface(holder, w, h); mRender = new SimpleModelRS(); - mRender.init(mRS, getResources(), w, h); + mRender.init(mRS, getResources()); } } @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + ensureRenderScript(); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + mRender.surfaceChanged(); + } + + @Override protected void onDetachedFromWindow() { + mRender = null; if (mRS != null) { mRS = null; destroyRenderScriptGL(); } } - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) - { - // break point at here - // this method doesn't work when 'extends View' include 'extends ScrollView'. - return super.onKeyDown(keyCode, event); + public void loadA3DFile(String path) { + mRender.loadA3DFile(path); } - @Override - public boolean onTouchEvent(MotionEvent ev) - { - boolean ret = true; - int act = ev.getAction(); - if (act == ev.ACTION_UP) { - ret = false; + public boolean onTouchEvent(MotionEvent ev) { + mScaleDetector.onTouchEvent(ev); + + boolean ret = false; + float x = ev.getX(); + float y = ev.getY(); + + final int action = ev.getAction(); + + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: { + mRender.onActionDown(x, y); + mActivePointerId = ev.getPointerId(0); + ret = true; + break; + } + case MotionEvent.ACTION_MOVE: { + if (!mScaleDetector.isInProgress()) { + mRender.onActionMove(x, y); + } + mRender.onActionDown(x, y); + ret = true; + break; + } + + case MotionEvent.ACTION_UP: { + mActivePointerId = INVALID_POINTER_ID; + break; + } + + case MotionEvent.ACTION_CANCEL: { + mActivePointerId = INVALID_POINTER_ID; + break; + } + + case MotionEvent.ACTION_POINTER_UP: { + final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + final int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == mActivePointerId) { + // This was our active pointer going up. Choose a new + // active pointer and adjust accordingly. + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + x = ev.getX(newPointerIndex); + y = ev.getY(newPointerIndex); + mRender.onActionDown(x, y); + mActivePointerId = ev.getPointerId(newPointerIndex); + } + break; + } } - mRender.touchEvent((int)ev.getX(), (int)ev.getY()); return ret; } + + private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + @Override + public boolean onScale(ScaleGestureDetector detector) { + mRender.onActionScale(detector.getScaleFactor()); + return true; + } + } } diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs index 419de62..1034b85 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2009 The Android Open Source Project +// Copyright (C) 2011 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. @@ -22,17 +22,112 @@ rs_program_vertex gPVBackground; rs_program_fragment gPFBackground; rs_allocation gTGrid; -rs_mesh gTestMesh; rs_program_store gPFSBackground; -float gRotate; - rs_font gItalic; rs_allocation gTextAlloc; +typedef struct MeshInfo { + rs_mesh mMesh; + int mNumIndexSets; + float3 bBoxMin; + float3 bBoxMax; +} MeshInfo_t; + +MeshInfo_t *gMeshes; + +static float3 gLookAt; + +static float gRotateX; +static float gRotateY; +static float gZoom; + +static float gLastX; +static float gLastY; + +void onActionDown(float x, float y) { + gLastX = x; + gLastY = y; +} + +void onActionScale(float scale) { + + gZoom *= 1.0f / scale; + gZoom = max(0.1f, min(gZoom, 500.0f)); +} + +void onActionMove(float x, float y) { + float dx = gLastX - x; + float dy = gLastY - y; + + if (fabs(dy) <= 2.0f) { + dy = 0.0f; + } + if (fabs(dx) <= 2.0f) { + dx = 0.0f; + } + + gRotateY -= dx; + if (gRotateY > 360) { + gRotateY -= 360; + } + if (gRotateY < 0) { + gRotateY += 360; + } + + gRotateX -= dy; + gRotateX = min(gRotateX, 80.0f); + gRotateX = max(gRotateX, -80.0f); + + gLastX = x; + gLastY = y; +} + void init() { - gRotate = 0.0f; + gRotateX = 0.0f; + gRotateY = 0.0f; + gZoom = 50.0f; + gLookAt = 0.0f; +} + +void updateMeshInfo() { + rs_allocation allMeshes = rsGetAllocation(gMeshes); + int size = rsAllocationGetDimX(allMeshes); + gLookAt = 0.0f; + float minX, minY, minZ, maxX, maxY, maxZ; + for (int i = 0; i < size; i++) { + MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i); + rsgMeshComputeBoundingBox(info->mMesh, + &minX, &minY, &minZ, + &maxX, &maxY, &maxZ); + info->bBoxMin = (minX, minY, minZ); + info->bBoxMax = (maxX, maxY, maxZ); + gLookAt += (info->bBoxMin + info->bBoxMax)*0.5f; + } + gLookAt = gLookAt / (float)size; +} + +static void renderAllMeshes() { + rs_allocation allMeshes = rsGetAllocation(gMeshes); + int size = rsAllocationGetDimX(allMeshes); + gLookAt = 0.0f; + float minX, minY, minZ, maxX, maxY, maxZ; + for (int i = 0; i < size; i++) { + MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i); + rsgDrawMesh(info->mMesh); + } +} + +void drawDescription() { + uint width = rsgGetWidth(); + uint height = rsgGetHeight(); + int left = 0, right = 0, top = 0, bottom = 0; + + rsgBindFont(gItalic); + + rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom); + rsgDrawText(gTextAlloc, 2 -left, height - 2 + bottom); } int root(int launchID) { @@ -43,7 +138,7 @@ int root(int launchID) { rsgBindProgramVertex(gPVBackground); rs_matrix4x4 proj; float aspect = (float)rsgGetWidth() / (float)rsgGetHeight(); - rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f); + rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f); rsgProgramVertexLoadProjectionMatrix(&proj); rsgBindProgramFragment(gPFBackground); @@ -52,20 +147,15 @@ int root(int launchID) { rs_matrix4x4 matrix; rsMatrixLoadIdentity(&matrix); - // Position our model on the screen - rsMatrixTranslate(&matrix, 0.0f, -0.3f, -10.0f); - rsMatrixScale(&matrix, 0.2f, 0.2f, 0.2f); - rsMatrixRotate(&matrix, 25.0f, 1.0f, 0.0f, 0.0f); - rsMatrixRotate(&matrix, gRotate, 0.0f, 1.0f, 0.0f); + // Position our models on the screen + rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom); + rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f); + rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f); rsgProgramVertexLoadModelMatrix(&matrix); - rsgDrawMesh(gTestMesh); - - rsgFontColor(0.3f, 0.3f, 0.3f, 1.0f); - rsgDrawText("Renderscript model test", 30, 695); + renderAllMeshes(); - rsgBindFont(gItalic); - rsgDrawText(gTextAlloc, 30, 730); + drawDescription(); return 10; } diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 0d55f08..f9990bb 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -60,7 +60,12 @@ LOCAL_SHARED_LIBRARIES := \ libEGL \ libpixelflinger \ libhardware \ - libhardware_legacy + libhardware_legacy \ + libskia \ + libbinder + +LOCAL_C_INCLUDES := \ + external/skia/include/core LOCAL_MODULE:= libui diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index b8d59e6..90b954e 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -15,6 +15,16 @@ #include <ui/Input.h> +#include <math.h> + +#ifdef HAVE_ANDROID_OS +#include <binder/Parcel.h> + +#include "SkPoint.h" +#include "SkMatrix.h" +#include "SkScalar.h" +#endif + namespace android { static const char* CONFIGURATION_FILE_DIR[] = { @@ -237,6 +247,41 @@ void KeyEvent::initialize(const KeyEvent& from) { mEventTime = from.mEventTime; } + +// --- PointerCoords --- + +#ifdef HAVE_ANDROID_OS +status_t PointerCoords::readFromParcel(Parcel* parcel) { + bits = parcel->readInt32(); + + uint32_t count = __builtin_popcount(bits); + if (count > MAX_AXES) { + return BAD_VALUE; + } + + for (uint32_t i = 0; i < count; i++) { + values[i] = parcel->readInt32(); + } + return OK; +} + +status_t PointerCoords::writeToParcel(Parcel* parcel) const { + parcel->writeInt32(bits); + + uint32_t count = __builtin_popcount(bits); + for (uint32_t i = 0; i < count; i++) { + parcel->writeInt32(values[i]); + } + return OK; +} +#endif + +void PointerCoords::tooManyAxes(int axis) { + LOGW("Could not set value for axis %d because the PointerCoords structure is full and " + "cannot contain more than %d axis values.", axis, int(MAX_AXES)); +} + + // --- MotionEvent --- void MotionEvent::initialize( @@ -272,6 +317,33 @@ void MotionEvent::initialize( addSample(eventTime, pointerCoords); } +void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { + InputEvent::initialize(other->mDeviceId, other->mSource); + mAction = other->mAction; + mFlags = other->mFlags; + mEdgeFlags = other->mEdgeFlags; + mMetaState = other->mMetaState; + mXOffset = other->mXOffset; + mYOffset = other->mYOffset; + mXPrecision = other->mXPrecision; + mYPrecision = other->mYPrecision; + mDownTime = other->mDownTime; + mPointerIds = other->mPointerIds; + + if (keepHistory) { + mSampleEventTimes = other->mSampleEventTimes; + mSamplePointerCoords = other->mSamplePointerCoords; + } else { + mSampleEventTimes.clear(); + mSampleEventTimes.push(other->getEventTime()); + mSamplePointerCoords.clear(); + size_t pointerCount = other->getPointerCount(); + size_t historySize = other->getHistorySize(); + mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array() + + (historySize * pointerCount), pointerCount); + } +} + void MotionEvent::addSample( int64_t eventTime, const PointerCoords* pointerCoords) { @@ -279,11 +351,224 @@ void MotionEvent::addSample( mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } +const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { + return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; +} + +float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { + return getRawPointerCoords(pointerIndex)->getAxisValue(axis); +} + +float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { + float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); + switch (axis) { + case AINPUT_MOTION_AXIS_X: + value += mXOffset; + break; + case AINPUT_MOTION_AXIS_Y: + value += mYOffset; + break; + } + return value; +} + +const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( + size_t pointerIndex, size_t historicalIndex) const { + return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex]; +} + +float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const { + return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); +} + +float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const { + float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + switch (axis) { + case AINPUT_MOTION_AXIS_X: + value += mXOffset; + break; + case AINPUT_MOTION_AXIS_Y: + value += mYOffset; + break; + } + return value; +} + void MotionEvent::offsetLocation(float xOffset, float yOffset) { mXOffset += xOffset; mYOffset += yOffset; } +static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { + float* value = c.editAxisValue(axis); + if (value) { + *value *= scaleFactor; + } +} + +void MotionEvent::scale(float scaleFactor) { + mXOffset *= scaleFactor; + mYOffset *= scaleFactor; + mXPrecision *= scaleFactor; + mYPrecision *= scaleFactor; + + size_t numSamples = mSamplePointerCoords.size(); + for (size_t i = 0; i < numSamples; i++) { + PointerCoords& c = mSamplePointerCoords.editItemAt(i); + // No need to scale pressure or size since they are normalized. + // No need to scale orientation since it is meaningless to do so. + scaleAxisValue(c, AINPUT_MOTION_AXIS_X, scaleFactor); + scaleAxisValue(c, AINPUT_MOTION_AXIS_Y, scaleFactor); + scaleAxisValue(c, AINPUT_MOTION_AXIS_TOUCH_MAJOR, scaleFactor); + scaleAxisValue(c, AINPUT_MOTION_AXIS_TOUCH_MINOR, scaleFactor); + scaleAxisValue(c, AINPUT_MOTION_AXIS_TOOL_MAJOR, scaleFactor); + scaleAxisValue(c, AINPUT_MOTION_AXIS_TOOL_MINOR, scaleFactor); + } +} + +#ifdef HAVE_ANDROID_OS +static inline float transformAngle(const SkMatrix* matrix, float angleRadians) { + // Construct and transform a vector oriented at the specified clockwise angle from vertical. + // Coordinate system: down is increasing Y, right is increasing X. + SkPoint vector; + vector.fX = SkFloatToScalar(sinf(angleRadians)); + vector.fY = SkFloatToScalar(-cosf(angleRadians)); + matrix->mapVectors(& vector, 1); + + // Derive the transformed vector's clockwise angle from vertical. + float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(-vector.fY)); + if (result < - M_PI_2) { + result += M_PI; + } else if (result > M_PI_2) { + result -= M_PI; + } + return result; +} + +void MotionEvent::transform(const SkMatrix* matrix) { + float oldXOffset = mXOffset; + float oldYOffset = mYOffset; + + // The tricky part of this implementation is to preserve the value of + // rawX and rawY. So we apply the transformation to the first point + // then derive an appropriate new X/Y offset that will preserve rawX and rawY. + SkPoint point; + float rawX = getRawX(0); + float rawY = getRawY(0); + matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset), + & point); + float newX = SkScalarToFloat(point.fX); + float newY = SkScalarToFloat(point.fY); + float newXOffset = newX - rawX; + float newYOffset = newY - rawY; + + mXOffset = newXOffset; + mYOffset = newYOffset; + + // Apply the transformation to all samples. + size_t numSamples = mSamplePointerCoords.size(); + for (size_t i = 0; i < numSamples; i++) { + PointerCoords& c = mSamplePointerCoords.editItemAt(i); + float* xPtr = c.editAxisValue(AINPUT_MOTION_AXIS_X); + float* yPtr = c.editAxisValue(AINPUT_MOTION_AXIS_Y); + if (xPtr && yPtr) { + float x = *xPtr + oldXOffset; + float y = *yPtr + oldYOffset; + matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point); + *xPtr = SkScalarToFloat(point.fX) - newXOffset; + *yPtr = SkScalarToFloat(point.fY) - newYOffset; + } + + float* orientationPtr = c.editAxisValue(AINPUT_MOTION_AXIS_ORIENTATION); + if (orientationPtr) { + *orientationPtr = transformAngle(matrix, *orientationPtr); + } + } +} + +status_t MotionEvent::readFromParcel(Parcel* parcel) { + size_t pointerCount = parcel->readInt32(); + size_t sampleCount = parcel->readInt32(); + if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0) { + return BAD_VALUE; + } + + mDeviceId = parcel->readInt32(); + mSource = parcel->readInt32(); + mAction = parcel->readInt32(); + mFlags = parcel->readInt32(); + mEdgeFlags = parcel->readInt32(); + mMetaState = parcel->readInt32(); + mXOffset = parcel->readFloat(); + mYOffset = parcel->readFloat(); + mXPrecision = parcel->readFloat(); + mYPrecision = parcel->readFloat(); + mDownTime = parcel->readInt64(); + + mPointerIds.clear(); + mPointerIds.setCapacity(pointerCount); + mSampleEventTimes.clear(); + mSampleEventTimes.setCapacity(sampleCount); + mSamplePointerCoords.clear(); + mSamplePointerCoords.setCapacity(sampleCount * pointerCount); + + for (size_t i = 0; i < pointerCount; i++) { + mPointerIds.push(parcel->readInt32()); + } + + while (sampleCount-- > 0) { + mSampleEventTimes.push(parcel->readInt64()); + for (size_t i = 0; i < pointerCount; i++) { + mSamplePointerCoords.push(); + status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); + if (!status) { + return status; + } + } + } + return OK; +} + +status_t MotionEvent::writeToParcel(Parcel* parcel) const { + size_t pointerCount = mPointerIds.size(); + size_t sampleCount = mSampleEventTimes.size(); + + parcel->writeInt32(pointerCount); + parcel->writeInt32(sampleCount); + + parcel->writeInt32(mDeviceId); + parcel->writeInt32(mSource); + parcel->writeInt32(mAction); + parcel->writeInt32(mFlags); + parcel->writeInt32(mEdgeFlags); + parcel->writeInt32(mMetaState); + parcel->writeFloat(mXOffset); + parcel->writeFloat(mYOffset); + parcel->writeFloat(mXPrecision); + parcel->writeFloat(mYPrecision); + parcel->writeInt64(mDownTime); + + for (size_t i = 0; i < pointerCount; i++) { + parcel->writeInt32(mPointerIds.itemAt(i)); + } + + const PointerCoords* pc = mSamplePointerCoords.array(); + for (size_t h = 0; h < sampleCount; h++) { + parcel->writeInt64(mSampleEventTimes.itemAt(h)); + for (size_t i = 0; i < pointerCount; i++) { + status_t status = (pc++)->writeToParcel(parcel); + if (!status) { + return status; + } + } + } + return OK; +} +#endif + + // --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { @@ -307,8 +592,8 @@ void InputDeviceInfo::initialize(int32_t id, const String8& name) { mMotionRanges.clear(); } -const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const { - ssize_t index = mMotionRanges.indexOfKey(rangeType); +const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t axis) const { + ssize_t index = mMotionRanges.indexOfKey(axis); return index >= 0 ? & mMotionRanges.valueAt(index) : NULL; } @@ -316,14 +601,14 @@ void InputDeviceInfo::addSource(uint32_t source) { mSources |= source; } -void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max, +void InputDeviceInfo::addMotionRange(int32_t axis, float min, float max, float flat, float fuzz) { MotionRange range = { min, max, flat, fuzz }; - addMotionRange(rangeType, range); + addMotionRange(axis, range); } -void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) { - mMotionRanges.add(rangeType, range); +void InputDeviceInfo::addMotionRange(int32_t axis, const MotionRange& range) { + mMotionRanges.add(axis, range); } } // namespace android diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp index 903fcaf..1819a8b 100644 --- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -159,15 +159,17 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( sampleEventTimes.push(i + 10); for (size_t j = 0; j < pointerCount; j++) { samplePointerCoords.push(); - samplePointerCoords.editTop().x = 100 * i + j; - samplePointerCoords.editTop().y = 200 * i + j; - samplePointerCoords.editTop().pressure = 0.5 * i + j; - samplePointerCoords.editTop().size = 0.7 * i + j; - samplePointerCoords.editTop().touchMajor = 1.5 * i + j; - samplePointerCoords.editTop().touchMinor = 1.7 * i + j; - samplePointerCoords.editTop().toolMajor = 2.5 * i + j; - samplePointerCoords.editTop().toolMinor = 2.7 * i + j; - samplePointerCoords.editTop().orientation = 3.5 * i + j; + PointerCoords& pc = samplePointerCoords.editTop(); + pc.clear(); + pc.setAxisValue(AINPUT_MOTION_AXIS_X, 100 * i + j); + pc.setAxisValue(AINPUT_MOTION_AXIS_Y, 200 * i + j); + pc.setAxisValue(AINPUT_MOTION_AXIS_PRESSURE, 0.5 * i + j); + pc.setAxisValue(AINPUT_MOTION_AXIS_SIZE, 0.7 * i + j); + pc.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR, 1.5 * i + j); + pc.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR, 1.7 * i + j); + pc.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, 2.5 * i + j); + pc.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, 2.7 * i + j); + pc.setAxisValue(AINPUT_MOTION_AXIS_ORIENTATION, 3.5 * i + j); } } @@ -239,27 +241,27 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); size_t offset = sampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].x, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X), motionEvent->getHistoricalRawX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].y, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y), motionEvent->getHistoricalRawY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].x + xOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X) + xOffset, motionEvent->getHistoricalX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].y + yOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y) + yOffset, motionEvent->getHistoricalY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].pressure, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE), motionEvent->getHistoricalPressure(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].size, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_SIZE), motionEvent->getHistoricalSize(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].touchMajor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR), motionEvent->getHistoricalTouchMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].touchMinor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR), motionEvent->getHistoricalTouchMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].toolMajor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR), motionEvent->getHistoricalToolMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].toolMinor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR), motionEvent->getHistoricalToolMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].orientation, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION), motionEvent->getHistoricalOrientation(i, sampleIndex)); } } @@ -269,17 +271,28 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); size_t offset = lastSampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].x, motionEvent->getRawX(i)); - EXPECT_EQ(samplePointerCoords[offset].y, motionEvent->getRawY(i)); - EXPECT_EQ(samplePointerCoords[offset].x + xOffset, motionEvent->getX(i)); - EXPECT_EQ(samplePointerCoords[offset].y + yOffset, motionEvent->getY(i)); - EXPECT_EQ(samplePointerCoords[offset].pressure, motionEvent->getPressure(i)); - EXPECT_EQ(samplePointerCoords[offset].size, motionEvent->getSize(i)); - EXPECT_EQ(samplePointerCoords[offset].touchMajor, motionEvent->getTouchMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].touchMinor, motionEvent->getTouchMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].toolMajor, motionEvent->getToolMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].toolMinor, motionEvent->getToolMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].orientation, motionEvent->getOrientation(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X), + motionEvent->getRawX(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y), + motionEvent->getRawY(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X) + xOffset, + motionEvent->getX(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y) + yOffset, + motionEvent->getY(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE), + motionEvent->getPressure(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_SIZE), + motionEvent->getSize(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR), + motionEvent->getTouchMajor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR), + motionEvent->getTouchMinor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR), + motionEvent->getToolMajor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR), + motionEvent->getToolMinor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION), + motionEvent->getOrientation(i)); } status = mConsumer->sendFinishedSignal(false); @@ -328,7 +341,8 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsErr const size_t pointerCount = 1; int32_t pointerIds[pointerCount] = { 0 }; - PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; + PointerCoords pointerCoords[pointerCount]; + pointerCoords[0].clear(); status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); |