diff options
-rw-r--r-- | core/java/android/view/SurfaceControl.java | 14 | ||||
-rw-r--r-- | core/java/android/view/SurfaceView.java | 8 | ||||
-rw-r--r-- | core/jni/android_view_SurfaceControl.cpp | 11 | ||||
-rw-r--r-- | graphics/java/android/graphics/RenderNode.java | 20 | ||||
-rw-r--r-- | libs/hwui/DamageAccumulator.cpp | 15 | ||||
-rw-r--r-- | libs/hwui/DamageAccumulator.h | 3 | ||||
-rw-r--r-- | libs/hwui/RenderNode.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/RenderProperties.cpp | 1 | ||||
-rw-r--r-- | libs/hwui/TreeInfo.h | 2 | ||||
-rw-r--r-- | libs/hwui/jni/android_graphics_RenderNode.cpp | 37 | ||||
-rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 9 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java | 77 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java | 157 |
13 files changed, 353 insertions, 7 deletions
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 66b9617714a6..3e62fde07283 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -141,6 +141,9 @@ public final class SurfaceControl implements Parcelable { int layerStack); private static native void nativeSetBlurRegions(long transactionObj, long nativeObj, float[][] regions, int length); + private static native void nativeSetStretchEffect(long transactionObj, long nativeObj, + float left, float top, float right, float bottom, float vecX, float vecY, + float maxStretchAmount); private static native boolean nativeClearContentFrameStats(long nativeObject); private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats); @@ -2951,6 +2954,17 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ + public Transaction setStretchEffect(SurfaceControl sc, float left, float top, float right, + float bottom, float vecX, float vecY, float maxStretchAmount) { + checkPreconditions(sc); + nativeSetStretchEffect(mNativeObject, sc.mNativeObject, left, top, right, bottom, + vecX, vecY, maxStretchAmount); + return this; + } + + /** + * @hide + */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O) public Transaction setLayerStack(SurfaceControl sc, int layerStack) { checkPreconditions(sc); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 6eba83fee48c..ec7e4c1f6a8e 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1444,6 +1444,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } @Override + public void applyStretch(long frameNumber, float left, float top, float right, + float bottom, float vecX, float vecY, float maxStretch) { + mRtTransaction.setStretchEffect(mSurfaceControl, left, top, right, bottom, vecX, vecY, + maxStretch); + applyRtTransaction(frameNumber); + } + + @Override public void positionLost(long frameNumber) { if (DEBUG) { Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 7a3366acce27..ffd138015479 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -580,6 +580,15 @@ static void nativeSetBlurRegions(JNIEnv* env, jclass clazz, jlong transactionObj transaction->setBlurRegions(ctrl, blurRegionVector); } +static void nativeSetStretchEffect(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong nativeObject, jfloat left, jfloat top, jfloat right, + jfloat bottom, jfloat vecX, jfloat vecY, + jfloat maxStretchAmount) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject); + transaction->setStretchEffect(ctrl, left, top, right, bottom, vecX, vecY, maxStretchAmount); +} + static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint w, jint h) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); @@ -1754,6 +1763,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetLayerStack }, {"nativeSetBlurRegions", "(JJ[[FI)V", (void*)nativeSetBlurRegions }, + {"nativeSetStretchEffect", "(JJFFFFFFF)V", + (void*) nativeSetStretchEffect }, {"nativeSetShadowRadius", "(JJF)V", (void*)nativeSetShadowRadius }, {"nativeSetFrameRate", "(JJFIZ)V", diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index 4a92cf11fa5c..f6f770be3de8 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -272,6 +272,16 @@ public final class RenderNode { void positionChanged(long frameNumber, int left, int top, int right, int bottom); /** + * Call to apply a stretch effect to any child SurfaceControl layers + * + * TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls? + * + * @hide + */ + default void applyStretch(long frameNumber, float left, float top, float right, + float bottom, float vecX, float vecY, float maxStretch) { } + + /** * Called by native on RenderThread to notify that the view is no longer in the * draw tree. UI thread is blocked at this point. * @@ -312,6 +322,14 @@ public final class RenderNode { pul.positionLost(frameNumber); } } + + @Override + public void applyStretch(long frameNumber, float left, float top, float right, float bottom, + float vecX, float vecY, float maxStretch) { + for (PositionUpdateListener pul : mListeners) { + pul.applyStretch(frameNumber, left, top, right, bottom, vecX, vecY, maxStretch); + } + } } /** @@ -707,7 +725,7 @@ public final class RenderNode { if (1.0 < vecY || vecY < -1.0) { throw new IllegalArgumentException("vecY must be in the range [-1, 1], was " + vecY); } - if (top <= bottom || right <= left) { + if (top >= bottom || left >= right) { throw new IllegalArgumentException( "Stretch region must not be empty, got " + new RectF(left, top, right, bottom).toString()); diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index b39f4f20dc0d..0bf948006ea0 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -249,5 +249,20 @@ void DamageAccumulator::finish(SkRect* totalDirty) { 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 2faa9d012d66..89ee0e34055b 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -35,6 +35,7 @@ namespace uirenderer { struct DirtyStack; class RenderNode; class Matrix4; +class StretchEffect; class DamageAccumulator { PREVENT_COPY_AND_ASSIGN(DamageAccumulator); @@ -62,6 +63,8 @@ public: 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 44f54eef458f..f5b2675c7fe6 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -226,6 +226,9 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu 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 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu 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 8fba9cf21df1..0589f136b666 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -70,6 +70,7 @@ LayerProperties& LayerProperties::operator=(const LayerProperties& other) { 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 f2481f83767d..cc9094c8afe9 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -98,6 +98,8 @@ public: 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 80239687a7fb..5f60437b5cc7 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 @@ static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz, // ---------------------------------------------------------------------------- jmethodID gPositionListener_PositionChangedMethod; +jmethodID gPositionListener_ApplyStretchMethod; jmethodID gPositionListener_PositionLostMethod; static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, @@ -571,6 +573,11 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, 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 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, 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 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, 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 @@ int register_android_view_RenderNode(JNIEnv* env) { 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)); diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index c6c67feeed72..62ccb1a0b9db 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -400,6 +400,15 @@ </intent-filter> </activity> + <activity android:name="StretchySurfaceViewActivity" + android:label="SurfaceView/Stretchy Movement" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="com.android.test.hwui.TEST"/> + </intent-filter> + </activity> + <activity android:name="GetBitmapSurfaceViewActivity" android:label="SurfaceView/GetBitmap with Camera source" android:exported="true"> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java index 818d899413de..65d7363ad628 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java @@ -19,8 +19,13 @@ package com.android.test.hwui; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; +import android.graphics.PointF; +import android.graphics.RecordingCanvas; +import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.RenderNode; import android.os.Bundle; +import android.view.MotionEvent; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.ScrollView; @@ -38,7 +43,46 @@ public class PositionListenerActivity extends Activity { ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge); layout.addView(spinner); - ScrollView scrollingThing = new ScrollView(this); + ScrollView scrollingThing = new ScrollView(this) { + int setting = 0; + PointF opts[] = new PointF[] { + new PointF(0, 0), + new PointF(0, -1f), + new PointF(1f, 0), + new PointF(0, 1f), + new PointF(-1f, 0), + new PointF(-1f, 1f), + }; + { + setWillNotDraw(false); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (ev.getActionMasked() == MotionEvent.ACTION_UP) { + setting = (setting + 1) % opts.length; + invalidate(); + } + return super.onTouchEvent(ev); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + RenderNode node = ((RecordingCanvas) canvas).mNode; + PointF dir = opts[setting]; + float maxStretchAmount = 100f; + // 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); + } + if (dir.y != 0f) { + node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(), + 0f, dir.y, maxStretchAmount); + } + } + }; scrollingThing.addView(new MyPositionReporter(this)); layout.addView(scrollingThing); @@ -49,6 +93,11 @@ public class PositionListenerActivity extends Activity { RenderNode mNode; int mCurrentCount = 0; int mTranslateY = 0; + Rect mPosition = new Rect(); + RectF mStretchArea = new RectF(); + float mStretchX = 0.0f; + float mStretchY = 0.0f; + float mStretchMax = 0.0f; MyPositionReporter(Context c) { super(c); @@ -78,18 +127,36 @@ public class PositionListenerActivity extends Activity { canvas.drawRenderNode(mNode); } + void updateText() { + setText(String.format("%d: Position %s, stretch area %s, vec %f,%f, amount %f", + mCurrentCount, mPosition.toShortString(), mStretchArea.toShortString(), + mStretchX, mStretchY, mStretchMax)); + } + @Override public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { - post(() -> { + getHandler().postAtFrontOfQueue(() -> { mCurrentCount++; - setText(String.format("%d: Position [%d, %d, %d, %d]", mCurrentCount, - left, top, right, bottom)); + mPosition.set(left, top, right, bottom); + updateText(); + }); + } + + @Override + public void applyStretch(long frameNumber, float left, float top, float right, float bottom, + float vecX, float vecY, float maxStretch) { + getHandler().postAtFrontOfQueue(() -> { + mStretchArea.set(left, top, right, bottom); + mStretchX = vecX; + mStretchY = vecY; + mStretchMax = maxStretch; + updateText(); }); } @Override public void positionLost(long frameNumber) { - post(() -> { + getHandler().postAtFrontOfQueue(() -> { mCurrentCount++; setText(mCurrentCount + " No position"); }); diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java new file mode 100644 index 000000000000..d6042445c2d8 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2021 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.test.hwui; + +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RecordingCanvas; +import android.graphics.RenderNode; +import android.os.Bundle; +import android.view.Gravity; +import android.view.SurfaceHolder; +import android.view.SurfaceHolder.Callback; +import android.view.SurfaceView; +import android.view.animation.LinearInterpolator; +import android.widget.FrameLayout; + +public class StretchySurfaceViewActivity extends Activity implements Callback { + SurfaceView mSurfaceView; + ObjectAnimator mAnimator; + + class MySurfaceView extends SurfaceView { + boolean mSlow; + boolean mScaled; + int mToggle = 0; + + public MySurfaceView(Context context) { + super(context); + setOnClickListener(v -> { + mToggle = (mToggle + 1) % 4; + mSlow = (mToggle & 0x2) != 0; + mScaled = (mToggle & 0x1) != 0; + + mSurfaceView.setScaleX(mScaled ? 1.6f : 1f); + mSurfaceView.setScaleY(mScaled ? 0.8f : 1f); + + setTitle("Slow=" + mSlow + ", scaled=" + mScaled); + invalidate(); + }); + setWillNotDraw(false); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + if (mSlow) { + try { + Thread.sleep(16); + } catch (InterruptedException e) {} + } + } + + public void setMyTranslationY(float ty) { + setTranslationY(ty); + if (mSlow) { + invalidate(); + } + } + + public float getMyTranslationY() { + return getTranslationY(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FrameLayout content = new FrameLayout(this) { + { + setWillNotDraw(false); + } + + @Override + protected void onDraw(Canvas canvas) { + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(Color.RED); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(10f); + canvas.drawLine(0f, 0f, getWidth(), getHeight(), paint); + super.onDraw(canvas); + + RenderNode node = ((RecordingCanvas) canvas).mNode; + node.stretch(0f, 0f, getWidth(), getHeight() / 2f, 0f, 1f, 400f); + } + }; + + mSurfaceView = new MySurfaceView(this); + mSurfaceView.getHolder().addCallback(this); + + final float density = getResources().getDisplayMetrics().density; + int size = (int) (200 * density); + + content.addView(mSurfaceView, new FrameLayout.LayoutParams( + size, size, Gravity.CENTER_HORIZONTAL | Gravity.TOP)); + mAnimator = ObjectAnimator.ofFloat(mSurfaceView, "myTranslationY", + 0, size); + mAnimator.setRepeatMode(ObjectAnimator.REVERSE); + mAnimator.setRepeatCount(ObjectAnimator.INFINITE); + mAnimator.setDuration(1000); + mAnimator.setInterpolator(new LinearInterpolator()); + setContentView(content); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Canvas canvas = holder.lockCanvas(); + canvas.drawColor(Color.WHITE); + + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(Color.GREEN); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(10f); + canvas.drawLine(0, 0, width, height, paint); + + holder.unlockCanvasAndPost(canvas); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + } + + @Override + protected void onResume() { + super.onResume(); + mAnimator.start(); + } + + @Override + protected void onPause() { + mAnimator.pause(); + super.onPause(); + } +} |