Add MotionEvent Matrix transformations.
Fixed issued in ViewGroup's transformation of MotionEvents to ensure that the entire historical trace is transformed, not just the current pointer. Simplified the code in ViewGroup for splitting events across Views. The new code also handles the case where some pointers are dispatched to the ViewGroup in addition to its children whereas the previous code would drop some pointers on the floor. Change-Id: I56ac31903e1de8a9c376d9c935b7217b0c42d93e
This commit is contained in:
@@ -195957,6 +195957,19 @@
|
||||
<parameter name="y" type="float">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="transform"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="matrix" type="android.graphics.Matrix">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="writeToParcel"
|
||||
return="void"
|
||||
abstract="false"
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
@@ -347,6 +348,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
|
||||
private RuntimeException mRecycledLocation;
|
||||
private boolean mRecycled;
|
||||
|
||||
private native void nativeTransform(Matrix matrix);
|
||||
|
||||
private MotionEvent(int pointerCount, int sampleCount) {
|
||||
mPointerIdentifiers = new int[pointerCount];
|
||||
mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA];
|
||||
@@ -1413,6 +1416,19 @@ public final class MotionEvent extends InputEvent implements Parcelable {
|
||||
mYOffset = y - dataSamples[lastDataSampleIndex + SAMPLE_Y];
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a transformation matrix to all of the points in the event.
|
||||
*
|
||||
* @param matrix The transformation matrix to apply.
|
||||
*/
|
||||
public final void transform(Matrix matrix) {
|
||||
if (matrix == null) {
|
||||
throw new IllegalArgumentException("matrix must not be null");
|
||||
}
|
||||
|
||||
nativeTransform(matrix);
|
||||
}
|
||||
|
||||
private final void getPointerCoordsAtSampleIndex(int sampleIndex,
|
||||
PointerCoords outPointerCoords) {
|
||||
final float[] dataSamples = mDataSamples;
|
||||
|
||||
@@ -5760,6 +5760,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given point, in local coordinates is inside the view.
|
||||
*/
|
||||
/*package*/ final boolean pointInView(float localX, float localY) {
|
||||
return localX >= 0 && localX < (mRight - mLeft)
|
||||
&& localY >= 0 && localY < (mBottom - mTop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to determine whether the given point, in local coordinates,
|
||||
* is inside the view, where the area of the view is expanded by the slop factor.
|
||||
@@ -5767,7 +5775,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
* is still within the view.
|
||||
*/
|
||||
private boolean pointInView(float localX, float localY, float slop) {
|
||||
return localX > -slop && localY > -slop && localX < ((mRight - mLeft) + slop) &&
|
||||
return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
|
||||
localY < ((mBottom - mTop) + slop);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,8 @@
|
||||
#include "SkMatrix.h"
|
||||
#include "SkTemplates.h"
|
||||
|
||||
#include "Matrix.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
class SkMatrixGlue {
|
||||
@@ -403,10 +405,20 @@ static JNINativeMethod methods[] = {
|
||||
{"native_equals", "(II)Z", (void*) SkMatrixGlue::equals}
|
||||
};
|
||||
|
||||
static jfieldID sNativeInstanceField;
|
||||
|
||||
int register_android_graphics_Matrix(JNIEnv* env) {
|
||||
int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Matrix", methods,
|
||||
sizeof(methods) / sizeof(methods[0]));
|
||||
|
||||
jclass clazz = env->FindClass("android/graphics/Matrix");
|
||||
sNativeInstanceField = env->GetFieldID(clazz, "native_instance", "I");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj) {
|
||||
return reinterpret_cast<SkMatrix*>(env->GetIntField(matrixObj, sNativeInstanceField));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
30
core/jni/android/graphics/Matrix.h
Normal file
30
core/jni/android/graphics/Matrix.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.
|
||||
*/
|
||||
|
||||
#ifndef _ANDROID_GRAPHICS_MATRIX_H
|
||||
#define _ANDROID_GRAPHICS_MATRIX_H
|
||||
|
||||
#include "jni.h"
|
||||
#include "SkMatrix.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
/* Gets the underlying SkMatrix from a Matrix object. */
|
||||
extern SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROID_GRAPHICS_MATRIX_H
|
||||
@@ -22,10 +22,26 @@
|
||||
#include <utils/Log.h>
|
||||
#include <ui/Input.h>
|
||||
#include "android_view_MotionEvent.h"
|
||||
#include "android/graphics/Matrix.h"
|
||||
|
||||
#include <math.h>
|
||||
#include "SkMatrix.h"
|
||||
#include "SkScalar.h"
|
||||
|
||||
// Number of float items per entry in a DVM sample data array
|
||||
#define NUM_SAMPLE_DATA 9
|
||||
|
||||
#define SAMPLE_X 0
|
||||
#define SAMPLE_Y 1
|
||||
#define SAMPLE_PRESSURE 2
|
||||
#define SAMPLE_SIZE 3
|
||||
#define SAMPLE_TOUCH_MAJOR 4
|
||||
#define SAMPLE_TOUCH_MINOR 5
|
||||
#define SAMPLE_TOOL_MAJOR 6
|
||||
#define SAMPLE_TOOL_MINOR 7
|
||||
#define SAMPLE_ORIENTATION 8
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -238,8 +254,87 @@ void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void android_view_MotionEvent_nativeTransform(JNIEnv* env,
|
||||
jobject eventObj, jobject matrixObj) {
|
||||
SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
|
||||
|
||||
jfloat oldXOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mXOffset);
|
||||
jfloat oldYOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mYOffset);
|
||||
jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers);
|
||||
jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples);
|
||||
jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
|
||||
gMotionEventClassInfo.mDataSamples));
|
||||
jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
|
||||
|
||||
// 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;
|
||||
jfloat rawX = dataSamples[SAMPLE_X];
|
||||
jfloat rawY = dataSamples[SAMPLE_Y];
|
||||
matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset),
|
||||
& point);
|
||||
jfloat newX = SkScalarToFloat(point.fX);
|
||||
jfloat newY = SkScalarToFloat(point.fY);
|
||||
jfloat newXOffset = newX - rawX;
|
||||
jfloat newYOffset = newY - rawY;
|
||||
|
||||
dataSamples[SAMPLE_ORIENTATION] = transformAngle(matrix, dataSamples[SAMPLE_ORIENTATION]);
|
||||
|
||||
// Apply the transformation to all samples.
|
||||
jfloat* currentDataSample = dataSamples;
|
||||
jfloat* endDataSample = dataSamples + numPointers * numSamples * NUM_SAMPLE_DATA;
|
||||
for (;;) {
|
||||
currentDataSample += NUM_SAMPLE_DATA;
|
||||
if (currentDataSample == endDataSample) {
|
||||
break;
|
||||
}
|
||||
|
||||
jfloat x = currentDataSample[SAMPLE_X] + oldXOffset;
|
||||
jfloat y = currentDataSample[SAMPLE_Y] + oldYOffset;
|
||||
matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point);
|
||||
currentDataSample[SAMPLE_X] = SkScalarToFloat(point.fX) - newXOffset;
|
||||
currentDataSample[SAMPLE_Y] = SkScalarToFloat(point.fY) - newYOffset;
|
||||
|
||||
currentDataSample[SAMPLE_ORIENTATION] = transformAngle(matrix,
|
||||
currentDataSample[SAMPLE_ORIENTATION]);
|
||||
}
|
||||
|
||||
env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0);
|
||||
|
||||
env->SetFloatField(eventObj, gMotionEventClassInfo.mXOffset, newXOffset);
|
||||
env->SetFloatField(eventObj, gMotionEventClassInfo.mYOffset, newYOffset);
|
||||
|
||||
env->DeleteLocalRef(dataSampleArray);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static JNINativeMethod gMotionEventMethods[] = {
|
||||
/* name, signature, funcPtr */
|
||||
{ "nativeTransform",
|
||||
"(Landroid/graphics/Matrix;)V",
|
||||
(void*)android_view_MotionEvent_nativeTransform },
|
||||
};
|
||||
|
||||
#define FIND_CLASS(var, className) \
|
||||
var = env->FindClass(className); \
|
||||
LOG_FATAL_IF(! var, "Unable to find class " className); \
|
||||
@@ -258,6 +353,10 @@ void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
|
||||
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
|
||||
|
||||
int register_android_view_MotionEvent(JNIEnv* env) {
|
||||
int res = jniRegisterNativeMethods(env, "android/view/MotionEvent",
|
||||
gMotionEventMethods, NELEM(gMotionEventMethods));
|
||||
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
|
||||
|
||||
FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
|
||||
|
||||
GET_STATIC_METHOD_ID(gMotionEventClassInfo.obtain, gMotionEventClassInfo.clazz,
|
||||
|
||||
Reference in New Issue
Block a user