diff options
author | 2022-08-08 23:58:59 +0000 | |
---|---|---|
committer | 2022-09-07 19:32:23 +0000 | |
commit | 914e75086755f7be427d2b6a1ed96662a0a7049e (patch) | |
tree | 6e4432c40fae697c2b9b50e3adad481e915ad842 | |
parent | 7566f477d9ce28d64c0817835b68d3f3d328baac (diff) |
Support alpha for SurfaceView
Basically, this removes an excuse for apps to use TextureView for video
playback or one-off HDR images prior to HDR UI being ready everywhere.
The idea is that for Z-above, applying alpha is easy, because the
surface just needs to be have alpha modulated. Z-below is a little bit
more creative - the alpha is applied to the hole punch and drawn using
DST_OUT blending semantics. This allows for views underneath the
SurfaceView to blend with surface, while being occluded by views on top
of the SurfaceView.
There may need to be some complex view hierarchies that would be useful
to test, but simple view layouts seem to work.
Note that this is guarded with a target SDK check, to defend against
applications that are propagated alpha to child views while expecting
opaque SurfaceViews.
Bug: 241474646
Test: HWAccelerationTest doesn't look broken
Change-Id: Ibc14b18f1ce6f25250318db50275c6b8c972bade
-rw-r--r-- | core/java/android/view/SurfaceView.java | 126 | ||||
-rw-r--r-- | graphics/java/android/graphics/BaseCanvas.java | 7 | ||||
-rw-r--r-- | graphics/java/android/graphics/BaseRecordingCanvas.java | 7 | ||||
-rw-r--r-- | libs/hwui/SkiaCanvas.cpp | 8 | ||||
-rw-r--r-- | libs/hwui/SkiaCanvas.h | 2 | ||||
-rw-r--r-- | libs/hwui/hwui/Canvas.h | 2 | ||||
-rw-r--r-- | libs/hwui/jni/android_graphics_Canvas.cpp | 7 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/RenderNodeDrawable.cpp | 3 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 16 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaRecordingCanvas.h | 2 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/TransformCanvas.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp | 4 | ||||
-rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 9 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java | 168 |
14 files changed, 273 insertions, 94 deletions
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index b6c92e3fd264..586c1935bbef 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -107,6 +107,14 @@ import java.util.function.Consumer; * and scaling a SurfaceView on screen will not cause rendering artifacts. Such * artifacts may occur on previous versions of the platform when its window is * positioned asynchronously.</p> + * + * <p class="note"><strong>Note:</strong> Starting in platform version + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, SurfaceView will support arbitrary + * alpha blending. Prior platform versions ignored alpha values on the SurfaceView if they were + * between 0 and 1. If the SurfaceView is configured with Z-above, then the alpha is applied + * directly to the Surface. If the SurfaceView is configured with Z-below, then the alpha is + * applied to the hole punch directly. Note that when using Z-below, overlapping SurfaceViews + * may not blend properly as a consequence of not applying alpha to the surface content directly. */ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCallback { private static final String TAG = "SurfaceView"; @@ -146,6 +154,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall Paint mRoundedViewportPaint; int mSubLayer = APPLICATION_MEDIA_SUBLAYER; + int mRequestedSubLayer = APPLICATION_MEDIA_SUBLAYER; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) boolean mIsCreating = false; @@ -177,8 +186,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @UnsupportedAppUsage int mRequestedFormat = PixelFormat.RGB_565; - boolean mUseAlpha = false; - float mSurfaceAlpha = 1f; + float mAlpha = 1f; boolean mClipSurfaceToBounds; int mBackgroundColor = Color.BLACK; @@ -335,58 +343,25 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * @hide */ public void setUseAlpha() { - if (!mUseAlpha) { - mUseAlpha = true; - updateSurfaceAlpha(); - } + // TODO(b/241474646): Remove me + return; } @Override public void setAlpha(float alpha) { - // Sets the opacity of the view to a value, where 0 means the view is completely transparent - // and 1 means the view is completely opaque. - // - // Note: Alpha value of this view is ignored by default. To enable alpha blending, you need - // to call setUseAlpha() as well. - // This view doesn't support translucent opacity if the view is located z-below, since the - // logic to punch a hole in the view hierarchy cannot handle such case. See also - // #clearSurfaceViewPort(Canvas) if (DEBUG) { Log.d(TAG, System.identityHashCode(this) - + " setAlpha: mUseAlpha = " + mUseAlpha + " alpha=" + alpha); + + " setAlpha: alpha=" + alpha); } super.setAlpha(alpha); - updateSurfaceAlpha(); - } - - private float getFixedAlpha() { - // Compute alpha value to be set on the underlying surface. - final float alpha = getAlpha(); - return mUseAlpha && (mSubLayer > 0 || alpha == 0f) ? alpha : 1f; } - private void updateSurfaceAlpha() { - if (!mUseAlpha || !mHaveFrame || mSurfaceControl == null) { - return; - } - final float viewAlpha = getAlpha(); - if (mSubLayer < 0 && 0f < viewAlpha && viewAlpha < 1f) { - Log.w(TAG, System.identityHashCode(this) - + " updateSurfaceAlpha:" - + " translucent color is not supported for a surface placed z-below."); - } - final ViewRootImpl viewRoot = getViewRootImpl(); - if (viewRoot == null) { - return; - } - final float alpha = getFixedAlpha(); - if (alpha != mSurfaceAlpha) { - final Transaction transaction = new Transaction(); - transaction.setAlpha(mSurfaceControl, alpha); - viewRoot.applyTransactionOnDraw(transaction); - damageInParent(); - mSurfaceAlpha = alpha; + @Override + protected boolean onSetAlpha(int alpha) { + if (Math.round(mAlpha * 255) != alpha) { + updateSurface(); } + return true; } private void performDrawFinished() { @@ -534,7 +509,15 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall invalidate(); } + @Override + public boolean hasOverlappingRendering() { + // SurfaceViews only alpha composite by modulating the Surface alpha for Z-above, or + // applying alpha on the hole punch for Z-below - no deferral to a layer is necessary. + return false; + } + private void clearSurfaceViewPort(Canvas canvas) { + final float alpha = getAlpha(); if (mCornerRadius > 0f) { canvas.getClipBounds(mTmpRect); if (mClipSurfaceToBounds && mClipBounds != null) { @@ -546,10 +529,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mTmpRect.right, mTmpRect.bottom, mCornerRadius, - mCornerRadius + mCornerRadius, + alpha ); } else { - canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f); + canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f, alpha); } } @@ -652,10 +636,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } else { subLayer = APPLICATION_MEDIA_SUBLAYER; } - if (mSubLayer == subLayer) { + if (mRequestedSubLayer == subLayer) { return false; } - mSubLayer = subLayer; + mRequestedSubLayer = subLayer; if (!allowDynamicChange) { return false; @@ -667,9 +651,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall if (viewRoot == null) { return true; } - final Transaction transaction = new SurfaceControl.Transaction(); - updateRelativeZ(transaction); - viewRoot.applyTransactionOnDraw(transaction); + + updateSurface(); invalidate(); return true; } @@ -722,8 +705,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private void releaseSurfaces(boolean releaseSurfacePackage) { - mSurfaceAlpha = 1f; - + mAlpha = 1f; synchronized (mSurfaceControlLock) { mSurface.destroy(); if (mBlastBufferQueue != null) { @@ -770,7 +752,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator, - boolean creating, boolean sizeChanged, boolean hintChanged, + boolean creating, boolean sizeChanged, boolean hintChanged, boolean relativeZChanged, Transaction surfaceUpdateTransaction) { boolean realSizeChanged = false; @@ -800,14 +782,20 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall surfaceUpdateTransaction.hide(mSurfaceControl); } - - updateBackgroundVisibility(surfaceUpdateTransaction); updateBackgroundColor(surfaceUpdateTransaction); - if (mUseAlpha) { - float alpha = getFixedAlpha(); + if (isAboveParent()) { + float alpha = getAlpha(); surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha); - mSurfaceAlpha = alpha; + } + + if (relativeZChanged) { + if (!isAboveParent()) { + // If we're moving from z-above to z-below, then restore the surface alpha back to 1 + // and let the holepunch drive visibility and blending. + surfaceUpdateTransaction.setAlpha(mSurfaceControl, 1.f); + } + updateRelativeZ(surfaceUpdateTransaction); } surfaceUpdateTransaction.setCornerRadius(mSurfaceControl, mCornerRadius); @@ -873,6 +861,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } finally { mSurfaceLock.unlock(); } + return realSizeChanged; } @@ -906,10 +895,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); - final float alpha = getFixedAlpha(); + final float alpha = getAlpha(); final boolean formatChanged = mFormat != mRequestedFormat; final boolean visibleChanged = mVisible != mRequestedVisible; - final boolean alphaChanged = mSurfaceAlpha != alpha; + final boolean alphaChanged = mAlpha != alpha; final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged) && mRequestedVisible; final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight; @@ -921,17 +910,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall || getHeight() != mScreenRect.height(); final boolean hintChanged = (viewRoot.getBufferTransformHint() != mTransformHint) && mRequestedVisible; + final boolean relativeZChanged = mSubLayer != mRequestedSubLayer; - if (creating || formatChanged || sizeChanged || visibleChanged || - (mUseAlpha && alphaChanged) || windowVisibleChanged || - positionChanged || layoutSizeChanged || hintChanged) { + if (creating || formatChanged || sizeChanged || visibleChanged + || alphaChanged || windowVisibleChanged || positionChanged + || layoutSizeChanged || hintChanged || relativeZChanged) { if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged + " visible=" + visibleChanged + " alpha=" + alphaChanged + " hint=" + hintChanged - + " mUseAlpha=" + mUseAlpha + " visible=" + visibleChanged + " left=" + (mWindowSpaceLeft != mLocation[0]) + " top=" + (mWindowSpaceTop != mLocation[1])); @@ -943,8 +932,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mSurfaceWidth = myWidth; mSurfaceHeight = myHeight; mFormat = mRequestedFormat; + mAlpha = alpha; mLastWindowVisibility = mWindowVisibility; mTransformHint = viewRoot.getBufferTransformHint(); + mSubLayer = mRequestedSubLayer; mScreenRect.left = mWindowSpaceLeft; mScreenRect.top = mWindowSpaceTop; @@ -968,7 +959,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } final boolean redrawNeeded = sizeChanged || creating || hintChanged - || (mVisible && !mDrawFinished); + || (mVisible && !mDrawFinished) || alphaChanged || relativeZChanged; boolean shouldSyncBuffer = redrawNeeded && viewRoot.wasRelayoutRequested() && viewRoot.isInLocalSync(); SyncBufferTransactionCallback syncBufferTransactionCallback = null; @@ -979,8 +970,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall syncBufferTransactionCallback::onTransactionReady); } - final boolean realSizeChanged = performSurfaceTransaction(viewRoot, - translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction); + final boolean realSizeChanged = performSurfaceTransaction(viewRoot, translator, + creating, sizeChanged, hintChanged, relativeZChanged, + surfaceUpdateTransaction); try { SurfaceHolder.Callback[] callbacks = null; diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index a8ab6d9e494e..54d64280c6f7 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -670,8 +670,9 @@ public abstract class BaseCanvas { /** * @hide */ - public void punchHole(float left, float top, float right, float bottom, float rx, float ry) { - nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry); + public void punchHole(float left, float top, float right, float bottom, float rx, float ry, + float alpha) { + nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, alpha); } /** @@ -823,5 +824,5 @@ public abstract class BaseCanvas { float hOffset, float vOffset, int flags, long nativePaint); private static native void nPunchHole(long renderer, float left, float top, float right, - float bottom, float rx, float ry); + float bottom, float rx, float ry, float alpha); } diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java index d06f665631cc..1ba79b87e87c 100644 --- a/graphics/java/android/graphics/BaseRecordingCanvas.java +++ b/graphics/java/android/graphics/BaseRecordingCanvas.java @@ -610,8 +610,9 @@ public class BaseRecordingCanvas extends Canvas { * @hide */ @Override - public void punchHole(float left, float top, float right, float bottom, float rx, float ry) { - nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry); + public void punchHole(float left, float top, float right, float bottom, float rx, float ry, + float alpha) { + nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, alpha); } @FastNative @@ -742,5 +743,5 @@ public class BaseRecordingCanvas extends Canvas { @FastNative private static native void nPunchHole(long renderer, float left, float top, float right, - float bottom, float rx, float ry); + float bottom, float rx, float ry, float alpha); } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 023d6bf0b673..20c6d6846fa2 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -18,6 +18,7 @@ #include "CanvasProperty.h" #include "NinePatchUtils.h" +#include "SkBlendMode.h" #include "VectorDrawable.h" #include "hwui/Bitmap.h" #include "hwui/MinikinUtils.h" @@ -251,10 +252,11 @@ const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const { return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr; } -void SkiaCanvas::punchHole(const SkRRect& rect) { +void SkiaCanvas::punchHole(const SkRRect& rect, float alpha) { SkPaint paint = SkPaint(); - paint.setColor(0); - paint.setBlendMode(SkBlendMode::kClear); + paint.setColor(SkColors::kBlack); + paint.setAlphaf(alpha); + paint.setBlendMode(SkBlendMode::kDstOut); mCanvas->drawRRect(rect, paint); } diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index c6313f6c3a88..51007c52260d 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -65,7 +65,7 @@ public: LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ"); } - virtual void punchHole(const SkRRect& rect) override; + virtual void punchHole(const SkRRect& rect, float alpha) override; virtual void setBitmap(const SkBitmap& bitmap) override; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 7378351ef771..82d23b51b12a 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -152,7 +152,7 @@ public: LOG_ALWAYS_FATAL("Not supported"); } - virtual void punchHole(const SkRRect& rect) = 0; + virtual void punchHole(const SkRRect& rect, float alpha) = 0; // ---------------------------------------------------------------------------- // Canvas state operations diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index fb7d5f72744d..0513447ed05e 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -713,9 +713,10 @@ static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) { } static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right, - jfloat bottom, jfloat rx, jfloat ry) { + jfloat bottom, jfloat rx, jfloat ry, jfloat alpha) { auto canvas = reinterpret_cast<Canvas*>(canvasPtr); - canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry)); + canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry), + alpha); } }; // namespace CanvasJNI @@ -790,7 +791,7 @@ static const JNINativeMethod gDrawMethods[] = { {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString}, {"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars}, {"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString}, - {"nPunchHole", "(JFFFFFF)V", (void*) CanvasJNI::punchHole} + {"nPunchHole", "(JFFFFFFF)V", (void*) CanvasJNI::punchHole} }; int register_android_graphics_Canvas(JNIEnv* env) { diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 3bf2b2e63a47..f2282e661535 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -201,6 +201,7 @@ protected: paint.setAlpha((uint8_t)paint.getAlpha() * mAlpha); return true; } + void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { // We unroll the drawable using "this" canvas, so that draw calls contained inside will // get their alpha applied. The default SkPaintFilterCanvas::onDrawDrawable does not unroll. @@ -292,7 +293,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // with the same canvas transformation + clip into the target // canvas then draw the layer on top if (renderNode->hasHolePunches()) { - TransformCanvas transformCanvas(canvas, SkBlendMode::kClear); + TransformCanvas transformCanvas(canvas, SkBlendMode::kDstOut); displayList->draw(&transformCanvas); } canvas->drawImageRect(snapshotImage, SkRect::Make(srcBounds), diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 5c6117d86415..1f87865f2672 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -69,20 +69,22 @@ void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, in mDisplayList->setHasHolePunches(false); } -void SkiaRecordingCanvas::punchHole(const SkRRect& rect) { - // Add the marker annotation to allow HWUI to determine where the current - // clip/transformation should be applied +void SkiaRecordingCanvas::punchHole(const SkRRect& rect, float alpha) { + // Add the marker annotation to allow HWUI to determine the current + // clip/transformation and alpha should be applied SkVector vector = rect.getSimpleRadii(); - float data[2]; + float data[3]; data[0] = vector.x(); data[1] = vector.y(); + data[2] = alpha; mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(), - SkData::MakeWithCopy(data, 2 * sizeof(float))); + SkData::MakeWithCopy(data, sizeof(data))); // Clear the current rect within the layer itself SkPaint paint = SkPaint(); - paint.setColor(0); - paint.setBlendMode(SkBlendMode::kClear); + paint.setColor(SkColors::kBlack); + paint.setAlphaf(alpha); + paint.setBlendMode(SkBlendMode::kDstOut); mRecorder.drawRRect(rect, paint); mDisplayList->setHasHolePunches(true); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 89e3a2c24e1e..7844e2cc2a73 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -50,7 +50,7 @@ public: initDisplayList(renderNode, width, height); } - virtual void punchHole(const SkRRect& rect) override; + virtual void punchHole(const SkRRect& rect, float alpha) override; virtual void finishRecording(uirenderer::RenderNode* destination) override; std::unique_ptr<SkiaDisplayList> finishRecording(); diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp index 33160d05e2d0..c320df035d08 100644 --- a/libs/hwui/pipeline/skia/TransformCanvas.cpp +++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp @@ -29,13 +29,15 @@ using namespace android::uirenderer::skiapipeline; void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) { if (HOLE_PUNCH_ANNOTATION == key) { auto* rectParams = reinterpret_cast<const float*>(value->data()); - float radiusX = rectParams[0]; - float radiusY = rectParams[1]; + const float radiusX = rectParams[0]; + const float radiusY = rectParams[1]; + const float alpha = rectParams[2]; SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY); SkPaint paint; paint.setColor(SkColors::kBlack); paint.setBlendMode(mHolePunchBlendMode); + paint.setAlphaf(alpha); mWrappedCanvas->drawRRect(roundRect, paint); } } diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp index 59230a754f4e..7d3ca9642458 100644 --- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp @@ -138,7 +138,7 @@ private: roundRectPaint.setColor(Color::White); if (addHolePunch) { // Punch a hole but then cover it up, we don't want to actually see it - canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight))); + canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)), 1.f); } canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); @@ -235,4 +235,4 @@ class StretchyUniformLayerListViewHolePunch : public StretchyListViewAnimation { StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; } bool haveHolePunch() override { return true; } bool forceLayer() override { return true; } -};
\ No newline at end of file +}; diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index b0ccbd1cf22f..939c7de22356 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -436,6 +436,15 @@ </intent-filter> </activity> + <activity android:name="SurfaceViewAlphaActivity" + android:label="SurfaceView/SurfaceView with Alpha" + 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=".PenStylusActivity" android:label="Pen/Draw" android:exported="true"> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java new file mode 100644 index 000000000000..01fe6ae0518b --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java @@ -0,0 +1,168 @@ +/* + * Copyright 2022 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.app.Activity; +import android.graphics.Canvas; +import android.graphics.Color; +import android.os.Bundle; +import android.view.SurfaceHolder; +import android.view.SurfaceHolder.Callback; +import android.view.SurfaceView; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.SeekBar; +import android.widget.TextView; + +public class SurfaceViewAlphaActivity extends Activity implements Callback { + SurfaceView mSurfaceView; + + private enum ZOrder { + ABOVE, + BELOW + } + + private float mAlpha = 127f / 255f; + private ZOrder mZOrder = ZOrder.BELOW; + + + private String getAlphaText() { + return "Alpha: " + mAlpha; + } + + private void toggleZOrder() { + if (ZOrder.ABOVE.equals(mZOrder)) { + mZOrder = ZOrder.BELOW; + } else { + mZOrder = ZOrder.ABOVE; + } + } + + // Overlaps a blue view on the left, then the SurfaceView in the center, then a blue view on the + // right. + private void overlapViews(SurfaceView view, LinearLayout parent) { + float density = getResources().getDisplayMetrics().density; + int surfaceViewSize = (int) (200 * density); + int blueViewSize = (int) (surfaceViewSize * 2 / 3f); + int totalSize = (int) (surfaceViewSize * 5 / 3f); + + RelativeLayout overlapLayout = new RelativeLayout(this); + + RelativeLayout.LayoutParams leftViewLayoutParams = new RelativeLayout.LayoutParams( + blueViewSize, surfaceViewSize); + leftViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + + View leftBlueView = new View(this); + leftBlueView.setBackgroundColor(Color.BLUE); + overlapLayout.addView(leftBlueView, leftViewLayoutParams); + + RelativeLayout.LayoutParams sVLayoutParams = new RelativeLayout.LayoutParams( + surfaceViewSize, surfaceViewSize); + sVLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); + overlapLayout.addView(view, sVLayoutParams); + + RelativeLayout.LayoutParams rightViewLayoutParams = new RelativeLayout.LayoutParams( + blueViewSize, surfaceViewSize); + rightViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + + View rightBlueView = new View(this); + rightBlueView.setBackgroundColor(Color.BLUE); + overlapLayout.addView(rightBlueView, rightViewLayoutParams); + + parent.addView(overlapLayout, new LinearLayout.LayoutParams( + totalSize, surfaceViewSize)); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mSurfaceView = new SurfaceView(this); + mSurfaceView.getHolder().addCallback(this); + mSurfaceView.setAlpha(mAlpha); + + LinearLayout content = new LinearLayout(this); + content.setOrientation(LinearLayout.VERTICAL); + + TextView alphaText = new TextView(this); + alphaText.setText(getAlphaText()); + + SeekBar alphaToggle = new SeekBar(this); + alphaToggle.setMin(0); + alphaToggle.setMax(255); + alphaToggle.setProgress(Math.round(mAlpha * 255)); + alphaToggle.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + mAlpha = progress / 255f; + alphaText.setText(getAlphaText()); + mSurfaceView.setAlpha(mAlpha); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + }); + + content.addView(alphaText, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + + content.addView(alphaToggle, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + + Button button = new Button(this); + button.setText("Z " + mZOrder.toString()); + button.setOnClickListener(v -> { + toggleZOrder(); + mSurfaceView.setZOrderOnTop(ZOrder.ABOVE.equals(mZOrder)); + button.setText("Z " + mZOrder.toString()); + }); + + content.addView(button, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + + overlapViews(mSurfaceView, content); + + 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.RED); + holder.unlockCanvasAndPost(canvas); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + } +} |