Updated EdgeEffect parameters for overscroll stretch

Added dampening logic to caluclate the distance to
overscroll.
Updated StretchEffect to support independent
maximum stretch amounts for the x/y axis

Removed debugging parameters to configure
stretch distance since they are no longer
used. Removed hidden API calls to configure
stretch distance in ScrollView/HorizontalScrollView

Bug: 179047472
Test: re-ran EdgeEffectTests
Change-Id: I4698669273d364695a21c2cce00ec2cfec41b2cc
This commit is contained in:
Nader Jawad
2021-03-17 15:12:58 -07:00
parent 78af4257ec
commit a225d21f7d
11 changed files with 67 additions and 107 deletions

View File

@@ -130,7 +130,11 @@ public class EdgeEffect {
public @interface EdgeEffectType {
}
private static final float DEFAULT_MAX_STRETCH_INTENSITY = 0.08f;
private static final float LINEAR_STRETCH_INTENSITY = 0.03f;
private static final float EXP_STRETCH_INTENSITY = 0.02f;
private static final float SCROLL_DIST_AFFECTED_BY_EXP_STRETCH = 0.4f;
@SuppressWarnings("UnusedDeclaration")
private static final String TAG = "EdgeEffect";
@@ -176,9 +180,6 @@ public class EdgeEffect {
private long mStartTime;
private float mDuration;
private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY;
private float mStretchDistanceFraction = 1f;
private float mStretchDistance = -1f;
private final Interpolator mInterpolator = new DecelerateInterpolator();
@@ -268,16 +269,6 @@ public class EdgeEffect {
mHeight = height;
}
/**
* Configure the distance in pixels to stretch the content. This is only consumed as part
* if {@link #setType(int)} is set to {@link #TYPE_STRETCH}
* @param stretchDistance Stretch distance in pixels when the target View is overscrolled
* @hide
*/
public void setStretchDistance(float stretchDistance) {
mStretchDistance = stretchDistance;
}
/**
* Reports if this EdgeEffect's animation is finished. If this method returns false
* after a call to {@link #draw(Canvas)} the host widget should schedule another
@@ -519,13 +510,6 @@ public class EdgeEffect {
mEdgeEffectType = type;
}
/**
* @hide
*/
public void setMaxStretchIntensity(float stretchIntensity) {
mStretchIntensity = stretchIntensity;
}
/**
* Set or clear the blend mode. A blend mode defines how source pixels
* (generated by a drawing command) are composited with the destination pixels
@@ -642,22 +626,19 @@ public class EdgeEffect {
// assume rotations of increments of 90 degrees
float x = mTmpPoints[10] - mTmpPoints[8];
float width = right - left;
float vecX = Math.max(-1f, Math.min(1f, x / width));
float vecX = dampStretchVector(Math.max(-1f, Math.min(1f, x / width)));
float y = mTmpPoints[11] - mTmpPoints[9];
float height = bottom - top;
float vecY = Math.max(-1f, Math.min(1f, y / height));
float vecY = dampStretchVector(Math.max(-1f, Math.min(1f, y / height)));
renderNode.stretch(
left,
top,
right,
bottom,
vecX * mStretchIntensity,
vecY * mStretchIntensity,
// TODO (njawad/mount) figure out proper stretch distance from UX
// for now leverage placeholder logic if no stretch distance is provided to
// consume the displacement ratio times the minimum of the width or height
mStretchDistance > 0 ? mStretchDistance :
(mStretchDistanceFraction * Math.max(mWidth, mHeight))
vecX,
vecY,
mWidth,
mHeight
);
}
@@ -794,4 +775,13 @@ public class EdgeEffect {
return Math.abs(mVelocity) < VELOCITY_THRESHOLD
&& Math.abs(displacement) < VALUE_THRESHOLD;
}
private float dampStretchVector(float normalizedVec) {
float sign = normalizedVec > 0 ? 1f : -1f;
float overscroll = Math.abs(normalizedVec);
float linearIntensity = LINEAR_STRETCH_INTENSITY * overscroll;
double scalar = Math.E / SCROLL_DIST_AFFECTED_BY_EXP_STRETCH;
double expIntensity = EXP_STRETCH_INTENSITY * (1 - Math.exp(-overscroll * scalar));
return sign * (float) (linearIntensity + expIntensity);
}
}

View File

@@ -248,26 +248,6 @@ public class HorizontalScrollView extends FrameLayout {
setRightEdgeEffectColor(color);
}
/**
* API used for prototyping stretch effect parameters in framework sample apps
* @hide
*/
public void setEdgeEffectIntensity(float intensity) {
mEdgeGlowLeft.setMaxStretchIntensity(intensity);
mEdgeGlowRight.setMaxStretchIntensity(intensity);
invalidate();
}
/**
* API used for prototyping stretch effect parameters in the framework sample apps
* @hide
*/
public void setStretchDistance(float distance) {
mEdgeGlowLeft.setStretchDistance(distance);
mEdgeGlowRight.setStretchDistance(distance);
invalidate();
}
/**
* Sets the right edge effect color.
*

View File

@@ -280,26 +280,6 @@ public class ScrollView extends FrameLayout {
setBottomEdgeEffectColor(color);
}
/**
* API used for prototyping stretch effect parameters in framework sample apps
* @hide
*/
public void setEdgeEffectIntensity(float intensity) {
mEdgeGlowTop.setMaxStretchIntensity(intensity);
mEdgeGlowBottom.setMaxStretchIntensity(intensity);
invalidate();
}
/**
* API used for prototyping stretch effect parameters in the framework sample apps
* @hide
*/
public void setStretchDistance(float distance) {
mEdgeGlowTop.setStretchDistance(distance);
mEdgeGlowBottom.setStretchDistance(distance);
invalidate();
}
/**
* Sets the bottom edge effect color.
*

View File

@@ -275,6 +275,8 @@ public final class RenderNode {
* Call to apply a stretch effect to any child SurfaceControl layers
*
* TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls?
* (njawad) update to consume different stretch parameters for horizontal/vertical stretch
* to ensure SkiaGLRenderEngine can also apply the same stretch to a surface
*
* @hide
*/
@@ -718,7 +720,7 @@ public final class RenderNode {
/** @hide */
public boolean stretch(float left, float top, float right, float bottom,
float vecX, float vecY, float maxStretchAmount) {
float vecX, float vecY, float maxStretchAmountX, float maxStretchAmountY) {
if (Float.isInfinite(vecX) || Float.isNaN(vecX)) {
throw new IllegalArgumentException("vecX must be a finite, non-NaN value " + vecX);
}
@@ -730,9 +732,13 @@ public final class RenderNode {
"Stretch region must not be empty, got "
+ new RectF(left, top, right, bottom).toString());
}
if (maxStretchAmount <= 0.0f) {
if (maxStretchAmountX <= 0.0f) {
throw new IllegalArgumentException(
"The max stretch amount must be >0, got " + maxStretchAmount);
"The max horizontal stretch amount must be >0, got " + maxStretchAmountX);
}
if (maxStretchAmountY <= 0.0f) {
throw new IllegalArgumentException(
"The max vertical stretch amount must be >0, got " + maxStretchAmountY);
}
return nStretch(
mNativeRenderNode,
@@ -742,7 +748,8 @@ public final class RenderNode {
bottom,
vecX,
vecY,
maxStretchAmount
maxStretchAmountX,
maxStretchAmountY
);
}
@@ -1695,7 +1702,7 @@ public final class RenderNode {
@CriticalNative
private static native boolean nStretch(long renderNode, float left, float top, float right,
float bottom, float vecX, float vecY, float maxStretch);
float bottom, float vecX, float vecY, float maxStretchX, float maxStretchY);
@CriticalNative
private static native boolean nHasShadow(long renderNode);

View File

@@ -33,7 +33,8 @@ static const SkString stretchShader = SkString(R"(
uniform float uMaxStretchIntensity;
// Maximum percentage to stretch beyond bounds of target
uniform float uStretchAffectedDist;
uniform float uStretchAffectedDistX;
uniform float uStretchAffectedDistY;
// Distance stretched as a function of the normalized overscroll times
// scale intensity
@@ -138,7 +139,7 @@ static const SkString stretchShader = SkString(R"(
outU,
inU,
uOverscrollX,
uStretchAffectedDist,
uStretchAffectedDistX,
uDistanceStretchedX,
uDistDiffX
);
@@ -146,7 +147,7 @@ static const SkString stretchShader = SkString(R"(
outV,
inV,
uOverscrollY,
uStretchAffectedDist,
uStretchAffectedDistY,
uDistanceStretchedY,
uDistDiffY
);
@@ -166,16 +167,14 @@ sk_sp<SkImageFilter> StretchEffect::getImageFilter(const sk_sp<SkImage>& snapsho
return mStretchFilter;
}
float distanceNotStretchedX = maxStretchAmount / stretchArea.width();
float distanceNotStretchedY = maxStretchAmount / stretchArea.height();
float normOverScrollDistX = mStretchDirection.x();
float normOverScrollDistY = mStretchDirection.y();
float distanceStretchedX = maxStretchAmount / (1 + abs(normOverScrollDistX));
float distanceStretchedY = maxStretchAmount / (1 + abs(normOverScrollDistY));
float diffX = distanceStretchedX - distanceNotStretchedX;
float diffY = distanceStretchedY - distanceNotStretchedY;
float viewportWidth = stretchArea.width();
float viewportHeight = stretchArea.height();
float normOverScrollDistX = mStretchDirection.x();
float normOverScrollDistY = mStretchDirection.y();
float distanceStretchedX = maxStretchAmountX / (1 + abs(normOverScrollDistX));
float distanceStretchedY = maxStretchAmountY / (1 + abs(normOverScrollDistY));
float diffX = distanceStretchedX;
float diffY = distanceStretchedY;
if (mBuilder == nullptr) {
mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
@@ -183,7 +182,8 @@ sk_sp<SkImageFilter> StretchEffect::getImageFilter(const sk_sp<SkImage>& snapsho
mBuilder->child("uContentTexture") = snapshotImage->makeShader(
SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
mBuilder->uniform("uStretchAffectedDist").set(&maxStretchAmount, 1);
mBuilder->uniform("uStretchAffectedDistX").set(&maxStretchAmountX, 1);
mBuilder->uniform("uStretchAffectedDistY").set(&maxStretchAmountY, 1);
mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
mBuilder->uniform("uDistDiffX").set(&diffX, 1);

View File

@@ -33,8 +33,12 @@ public:
SmoothStep,
};
StretchEffect(const SkRect& area, const SkVector& direction, float maxStretchAmount)
: stretchArea(area), maxStretchAmount(maxStretchAmount), mStretchDirection(direction) {}
StretchEffect(const SkRect& area, const SkVector& direction, float maxStretchAmountX,
float maxStretchAmountY)
: stretchArea(area)
, maxStretchAmountX(maxStretchAmountX)
, maxStretchAmountY(maxStretchAmountY)
, mStretchDirection(direction) {}
StretchEffect() {}
@@ -50,7 +54,8 @@ public:
this->stretchArea = other.stretchArea;
this->mStretchDirection = other.mStretchDirection;
this->mStretchFilter = nullptr;
this->maxStretchAmount = other.maxStretchAmount;
this->maxStretchAmountX = other.maxStretchAmountX;
this->maxStretchAmountY = other.maxStretchAmountY;
return *this;
}
@@ -67,13 +72,15 @@ public:
return setEmpty();
}
stretchArea.join(other.stretchArea);
maxStretchAmount = std::max(maxStretchAmount, other.maxStretchAmount);
maxStretchAmountX = std::max(maxStretchAmountX, other.maxStretchAmountX);
maxStretchAmountY = std::max(maxStretchAmountY, other.maxStretchAmountY);
}
sk_sp<SkImageFilter> getImageFilter(const sk_sp<SkImage>& snapshotImage) const;
SkRect stretchArea {0, 0, 0, 0};
float maxStretchAmount = 0;
float maxStretchAmountX = 0;
float maxStretchAmountY = 0;
void setStretchDirection(const SkVector& direction) {
mStretchFilter = nullptr;

View File

@@ -181,9 +181,10 @@ static jboolean android_view_RenderNode_clearStretch(CRITICAL_JNI_PARAMS_COMMA j
static jboolean android_view_RenderNode_stretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jfloat left, jfloat top, jfloat right,
jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
StretchEffect effect =
StretchEffect(SkRect::MakeLTRB(left, top, right, bottom), {.fX = vX, .fY = vY}, max);
jfloat bottom, jfloat vX, jfloat vY, jfloat maxX,
jfloat maxY) {
StretchEffect effect = StretchEffect(SkRect::MakeLTRB(left, top, right, bottom),
{.fX = vX, .fY = vY}, maxX, maxY);
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutateLayerProperties().mutableStretchEffect().mergeWith(
effect);
@@ -662,7 +663,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
info.canvasContext.getFrameNumber(), area.left, area.top,
area.right, area.bottom, stretchDirection.fX, stretchDirection.fY,
effect->maxStretchAmount);
effect->maxStretchAmountX, effect->maxStretchAmountY);
#endif
env->DeleteLocalRef(localref);
}
@@ -738,7 +739,7 @@ static const JNINativeMethod gMethods[] = {
{"nSetOutlineEmpty", "(J)Z", (void*)android_view_RenderNode_setOutlineEmpty},
{"nSetOutlineNone", "(J)Z", (void*)android_view_RenderNode_setOutlineNone},
{"nClearStretch", "(J)Z", (void*)android_view_RenderNode_clearStretch},
{"nStretch", "(JFFFFFFF)Z", (void*)android_view_RenderNode_stretch},
{"nStretch", "(JFFFFFFFF)Z", (void*)android_view_RenderNode_stretch},
{"nHasShadow", "(J)Z", (void*)android_view_RenderNode_hasShadow},
{"nSetSpotShadowColor", "(JI)Z", (void*)android_view_RenderNode_setSpotShadowColor},
{"nGetSpotShadowColor", "(J)I", (void*)android_view_RenderNode_getSpotShadowColor},

View File

@@ -18,8 +18,6 @@ package com.android.test.hwui;
import android.app.Activity;
import android.os.Bundle;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
public class EdgeEffectStretchActivity extends Activity {
@@ -27,10 +25,5 @@ public class EdgeEffectStretchActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.stretch_layout);
HorizontalScrollView hsv = findViewById(R.id.horizontal_scroll_view);
hsv.setStretchDistance(50f);
ScrollView sv = findViewById(R.id.scroll_view);
sv.setStretchDistance(50f);
}
}

View File

@@ -75,11 +75,11 @@ public class PositionListenerActivity extends Activity {
// Although we could do this in a single call, the real one won't be - so mimic that
if (dir.x != 0f) {
node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(),
dir.x, 0f, maxStretchAmount);
dir.x, 0f, maxStretchAmount, maxStretchAmount);
}
if (dir.y != 0f) {
node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(),
0f, dir.y, maxStretchAmount);
0f, dir.y, maxStretchAmount, maxStretchAmount);
}
}
};

View File

@@ -415,6 +415,7 @@ public class StretchShaderActivity extends Activity {
bounds.height(),
mOverScrollX,
mOverScrollY,
mStretchDistance,
mStretchDistance
);
}

View File

@@ -99,7 +99,8 @@ public class StretchySurfaceViewActivity extends Activity implements Callback {
super.onDraw(canvas);
RenderNode node = ((RecordingCanvas) canvas).mNode;
node.stretch(0f, 0f, getWidth(), getHeight() / 2f, 0f, 1f, 400f);
node.stretch(0f, 0f, getWidth(), getHeight() / 2f, 0f,
1f, 400f, 400f);
}
};