Merge "Revert^2 "Support alpha for SurfaceView""
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,7 +610,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
|
||||
* @hide
|
||||
*/
|
||||
public boolean isZOrderedOnTop() {
|
||||
return mSubLayer > 0;
|
||||
return mRequestedSubLayer > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user