Updated HWUI to calculate the stretch bounds for a surface
Added logic to StretchEffect to be able to calculate the
stretched position of a pixel on an input texture based
on the current stretch parameters.
Updated shader to leverage linear easing instead of
cubic in order to have a reversible method that
can be used to map input textures to the corresponding
stretch position.
Bug: 179047472
Test: manual
Change-Id: Id32afcf0df1cca03e68ac0c594d307a1090264f2
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 94fe243..cf3ef40b 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -272,18 +272,21 @@
DamageAccumulator::StretchResult DamageAccumulator::findNearestStretchEffect() const {
DirtyStack* frame = mHead;
+ const auto& headProperties = mHead->renderNode->properties();
+ float startWidth = headProperties.getWidth();
+ float startHeight = headProperties.getHeight();
while (frame->prev != frame) {
if (frame->type == TransformRenderNode) {
const auto& renderNode = frame->renderNode;
const auto& frameRenderNodeProperties = renderNode->properties();
const auto& effect =
frameRenderNodeProperties.layerProperties().getStretchEffect();
- const float width = (float) renderNode->getWidth();
- const float height = (float) renderNode->getHeight();
+ const float width = (float) frameRenderNodeProperties.getWidth();
+ const float height = (float) frameRenderNodeProperties.getHeight();
if (!effect.isEmpty()) {
Matrix4 stretchMatrix;
computeTransformImpl(mHead, frame, &stretchMatrix);
- Rect stretchRect = Rect(0.f, 0.f, width, height);
+ Rect stretchRect = Rect(0.f, 0.f, startWidth, startHeight);
stretchMatrix.mapRect(stretchRect);
return StretchResult{
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 1519d69..0599bfa 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -68,9 +68,8 @@
// and the other way around.
uniform float uInterpolationStrength;
- float easeInCubic(float t, float d) {
- float tmp = t * d;
- return tmp * tmp * tmp;
+ float easeIn(float t, float d) {
+ return t * d;
}
float computeOverscrollStart(
@@ -83,7 +82,7 @@
) {
float offsetPos = uStretchAffectedDist - inPos;
float posBasedVariation = mix(
- 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ 1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
float stretchIntensity = overscroll * posBasedVariation;
return distanceStretched - (offsetPos / (1. + stretchIntensity));
}
@@ -99,7 +98,7 @@
) {
float offsetPos = inPos - reverseStretchDist;
float posBasedVariation = mix(
- 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ 1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
float stretchIntensity = (-overscroll) * posBasedVariation;
return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
}
@@ -234,4 +233,80 @@
return instance.effect;
}
+/**
+ * Helper method that maps the input texture position to the stretch position
+ * based on the given overscroll value that represents an overscroll from
+ * either the top or left
+ * @param overscroll current overscroll value
+ * @param input normalized input position (can be x or y) on the input texture
+ * @return stretched position of the input normalized from 0 to 1
+ */
+float reverseMapStart(float overscroll, float input) {
+ float numerator = (-input * overscroll * overscroll) -
+ (2 * input * overscroll) - input;
+ float denominator = 1.f + (.3f * overscroll) +
+ (.7f * input * overscroll * overscroll) + (.7f * input * overscroll);
+ return -(numerator / denominator);
+}
+
+/**
+ * Helper method that maps the input texture position to the stretch position
+ * based on the given overscroll value that represents an overscroll from
+ * either the bottom or right
+ * @param overscroll current overscroll value
+ * @param input normalized input position (can be x or y) on the input texture
+ * @return stretched position of the input normalized from 0 to 1
+ */
+float reverseMapEnd(float overscroll, float input) {
+ float numerator = (.3f * overscroll * overscroll) -
+ (.3f * input * overscroll * overscroll) +
+ (1.3f * input * overscroll) - overscroll - input;
+ float denominator = (.7f * input * overscroll * overscroll) -
+ (.7f * input * overscroll) - (.7f * overscroll * overscroll) +
+ overscroll - 1.f;
+ return numerator / denominator;
+}
+
+/**
+ * Calculates the normalized stretch position given the normalized input
+ * position. This handles calculating the overscroll from either the
+ * top or left vs bottom or right depending on the sign of the given overscroll
+ * value
+ *
+ * @param overscroll unit vector of overscroll from -1 to 1 indicating overscroll
+ * from the bottom or right vs top or left respectively
+ * @param normalizedInput the
+ * @return
+ */
+float computeReverseOverscroll(float overscroll, float normalizedInput) {
+ float distanceStretched = 1.f / (1.f + abs(overscroll));
+ float distanceDiff = distanceStretched - 1.f;
+ if (overscroll > 0) {
+ float output = reverseMapStart(overscroll, normalizedInput);
+ if (output <= 1.0f) {
+ return output;
+ } else if (output >= distanceStretched){
+ return output - distanceDiff;
+ }
+ }
+
+ if (overscroll < 0) {
+ float output = reverseMapEnd(overscroll, normalizedInput);
+ if (output >= 0.f) {
+ return output;
+ } else if (output < 0.f){
+ return output + distanceDiff;
+ }
+ }
+ return normalizedInput;
+}
+
+float StretchEffect::computeStretchedPositionX(float normalizedX) const {
+ return computeReverseOverscroll(mStretchDirection.x(), normalizedX);
+}
+
+float StretchEffect::computeStretchedPositionY(float normalizedY) const {
+ return computeReverseOverscroll(mStretchDirection.y(), normalizedY);
+}
+
} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 61537f0..6d517ac 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -75,6 +75,22 @@
maxStretchAmountY = std::max(maxStretchAmountY, other.maxStretchAmountY);
}
+ /**
+ * Return the stretched x position given the normalized x position with
+ * the current horizontal stretch direction
+ * @param normalizedX x position on the input texture from 0 to 1
+ * @return x position when the horizontal stretch direction applied
+ */
+ float computeStretchedPositionX(float normalizedX) const;
+
+ /**
+ * Return the stretched y position given the normalized y position with
+ * the current horizontal stretch direction
+ * @param normalizedX y position on the input texture from 0 to 1
+ * @return y position when the horizontal stretch direction applied
+ */
+ float computeStretchedPositionY(float normalizedY) const;
+
sk_sp<SkShader> getShader(float width, float height,
const sk_sp<SkImage>& snapshotImage) const;
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 5131c64..f92c32c 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -572,11 +572,11 @@
info.damageAccumulator->computeCurrentTransform(&transform);
const RenderProperties& props = node.properties();
+ uirenderer::Rect bounds(props.getWidth(), props.getHeight());
if (info.stretchEffectCount) {
- handleStretchEffect(info, transform);
+ handleStretchEffect(info, bounds);
}
- uirenderer::Rect bounds(props.getWidth(), props.getHeight());
transform.mapRect(bounds);
if (CC_LIKELY(transform.isPureTranslate())) {
@@ -639,7 +639,33 @@
return env;
}
- void handleStretchEffect(const TreeInfo& info, const Matrix4& transform) {
+ void stretchTargetBounds(const StretchEffect& stretchEffect,
+ float width, float height,
+ const SkRect& childRelativeBounds,
+ uirenderer::Rect& bounds) {
+ float normalizedLeft = childRelativeBounds.left() / width;
+ float normalizedTop = childRelativeBounds.top() / height;
+ float normalizedRight = childRelativeBounds.right() / width;
+ float normalizedBottom = childRelativeBounds.bottom() / height;
+ float reverseLeft = width *
+ (stretchEffect.computeStretchedPositionX(normalizedLeft) -
+ normalizedLeft);
+ float reverseTop = height *
+ (stretchEffect.computeStretchedPositionY(normalizedTop) -
+ normalizedTop);
+ float reverseRight = width *
+ (stretchEffect.computeStretchedPositionX(normalizedRight) -
+ normalizedLeft);
+ float reverseBottom = height *
+ (stretchEffect.computeStretchedPositionY(normalizedBottom) -
+ normalizedTop);
+ bounds.left = reverseLeft;
+ bounds.top = reverseTop;
+ bounds.right = reverseRight;
+ bounds.bottom = reverseBottom;
+ }
+
+ void handleStretchEffect(const TreeInfo& info, uirenderer::Rect& targetBounds) {
// Search up to find the nearest stretcheffect parent
const DamageAccumulator::StretchResult result =
info.damageAccumulator->findNearestStretchEffect();
@@ -649,6 +675,8 @@
}
const auto& childRelativeBounds = result.childRelativeBounds;
+ stretchTargetBounds(*effect, result.width, result.height,
+ childRelativeBounds,targetBounds);
JNIEnv* env = jnienv();