Wire up SurfaceView stretch effect

Bug: 179047472
Test: StretchySurfaceViewActivity

Change-Id: I7f3d582cc66fb732a557e9332edc6d186db2335c
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index b39f4f2..0bf9480 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -249,5 +249,20 @@
     mHead->pendingDirty.setEmpty();
 }
 
+const StretchEffect* DamageAccumulator::findNearestStretchEffect() const {
+    DirtyStack* frame = mHead;
+    while (frame->prev != frame) {
+        frame = frame->prev;
+        if (frame->type == TransformRenderNode) {
+            const auto& effect =
+                    frame->renderNode->properties().layerProperties().getStretchEffect();
+            if (!effect.isEmpty()) {
+                return &effect;
+            }
+        }
+    }
+    return nullptr;
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 2faa9d0..89ee0e3 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -35,6 +35,7 @@
 struct DirtyStack;
 class RenderNode;
 class Matrix4;
+class StretchEffect;
 
 class DamageAccumulator {
     PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
@@ -62,6 +63,8 @@
 
     void finish(SkRect* totalDirty);
 
+    const StretchEffect* findNearestStretchEffect() const;
+
 private:
     void pushCommon();
     void applyMatrix4Transform(DirtyStack* frame);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 44f54ee..f5b2675 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -226,6 +226,9 @@
     if (!mProperties.getAllowForceDark()) {
         info.disableForceDark++;
     }
+    if (!mProperties.layerProperties().getStretchEffect().isEmpty()) {
+        info.stretchEffectCount++;
+    }
 
     uint32_t animatorDirtyMask = 0;
     if (CC_LIKELY(info.runAnimations)) {
@@ -267,6 +270,9 @@
     if (!mProperties.getAllowForceDark()) {
         info.disableForceDark--;
     }
+    if (!mProperties.layerProperties().getStretchEffect().isEmpty()) {
+        info.stretchEffectCount--;
+    }
     info.damageAccumulator->popTransform();
 }
 
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 8fba9cf..0589f13 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -70,6 +70,7 @@
     setXferMode(other.xferMode());
     setColorFilter(other.getColorFilter());
     setImageFilter(other.getImageFilter());
+    mStretchEffect = other.mStretchEffect;
     return *this;
 }
 
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index f2481f8..cc9094c 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -98,6 +98,8 @@
 
     const SkISize screenSize;
 
+    int stretchEffectCount = 0;
+
     struct Out {
         bool hasFunctors = false;
         // This is only updated if evaluateAnimations is true
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 8023968..5f60437 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -25,6 +25,7 @@
 #include <renderthread/CanvasContext.h>
 #endif
 #include <TreeInfo.h>
+#include <effects/StretchEffect.h>
 #include <hwui/Paint.h>
 #include <utils/TraceUtils.h>
 
@@ -549,6 +550,7 @@
 // ----------------------------------------------------------------------------
 
 jmethodID gPositionListener_PositionChangedMethod;
+jmethodID gPositionListener_ApplyStretchMethod;
 jmethodID gPositionListener_PositionLostMethod;
 
 static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
@@ -571,6 +573,11 @@
             Matrix4 transform;
             info.damageAccumulator->computeCurrentTransform(&transform);
             const RenderProperties& props = node.properties();
+
+            if (info.stretchEffectCount) {
+                handleStretchEffect(info, transform);
+            }
+
             uirenderer::Rect bounds(props.getWidth(), props.getHeight());
             transform.mapRect(bounds);
 
@@ -613,7 +620,7 @@
             JNIEnv* env = jnienv();
             jobject localref = env->NewLocalRef(mWeakRef);
             if (CC_UNLIKELY(!localref)) {
-                jnienv()->DeleteWeakGlobalRef(mWeakRef);
+                env->DeleteWeakGlobalRef(mWeakRef);
                 mWeakRef = nullptr;
                 return;
             }
@@ -634,6 +641,32 @@
             return env;
         }
 
+        void handleStretchEffect(const TreeInfo& info, const Matrix4& transform) {
+            // Search up to find the nearest stretcheffect parent
+            const StretchEffect* effect = info.damageAccumulator->findNearestStretchEffect();
+            if (!effect) {
+                return;
+            }
+
+            uirenderer::Rect area = effect->stretchArea;
+            transform.mapRect(area);
+            JNIEnv* env = jnienv();
+
+            jobject localref = env->NewLocalRef(mWeakRef);
+            if (CC_UNLIKELY(!localref)) {
+                env->DeleteWeakGlobalRef(mWeakRef);
+                mWeakRef = nullptr;
+                return;
+            }
+#ifdef __ANDROID__  // Layoutlib does not support CanvasContext
+            env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
+                                info.canvasContext.getFrameNumber(), area.left, area.top,
+                                area.right, area.bottom, effect->stretchDirection.fX,
+                                effect->stretchDirection.fY, effect->maxStretchAmount);
+#endif
+            env->DeleteLocalRef(localref);
+        }
+
         void doUpdatePositionAsync(jlong frameNumber, jint left, jint top,
                 jint right, jint bottom) {
             ATRACE_NAME("Update SurfaceView position");
@@ -775,6 +808,8 @@
     jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener");
     gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz,
             "positionChanged", "(JIIII)V");
+    gPositionListener_ApplyStretchMethod =
+            GetMethodIDOrDie(env, clazz, "applyStretch", "(JFFFFFFF)V");
     gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz,
             "positionLost", "(J)V");
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));