From 3ccaff2fc7399e2844d4ea14e2577dcbabecbbf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Franco?= Date: Mon, 19 Sep 2022 16:47:22 +0000 Subject: [PATCH] Revert "Using clippath for adaptive icon drawable instead of bit..." Revert "Fixing alpha tests for adaptive drawable" Revert submission 19370745-no-clip-adaptive_icon Reason for revert: Culprit cl for b/241010439 Reverted Changes: Ieb428748a:Fixing alpha tests for adaptive drawable I60ce42d91:Using clippath for adaptive icon drawable instead ... Change-Id: I70a55c49ee6929a20a8bcda83284f5fe59256be0 --- .../drawable/AdaptiveIconDrawableTest.java | 13 -- .../drawable/AdaptiveIconDrawable.java | 179 +++++++++++++----- 2 files changed, 133 insertions(+), 59 deletions(-) diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java index ddc05e09c395b..7338c3a3ea8ae 100644 --- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java +++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java @@ -204,19 +204,6 @@ public class AdaptiveIconDrawableTest extends AndroidTestCase { assertEquals(100, Color.alpha(bitmap.getPixel(50, 50))); } - @Test - public void testSetBounds() throws Exception { - mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable); - mIconDrawable.setBounds(0, 0, 100, 100); - - assertEquals(new Rect(-25, -25, 125, 125), mBackgroundDrawable.getBounds()); - assertEquals(new Rect(-25, -25, 125, 125), mForegroundDrawable.getBounds()); - - mIconDrawable.setBounds(10, 10, 110, 110); - assertEquals(new Rect(-15, -15, 135, 135), mBackgroundDrawable.getBounds()); - assertEquals(new Rect(-15, -15, 135, 135), mForegroundDrawable.getBounds()); - } - // // Utils // diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java index 6939a72019e84..2f56b181f4557 100644 --- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java +++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java @@ -26,6 +26,8 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Color; @@ -37,6 +39,8 @@ import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.Shader; +import android.graphics.Shader.TileMode; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.PathParser; @@ -102,8 +106,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback private static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE); /** - * Unused path. - * TODO: Remove once the layoutLib is updated + * Clip path defined in R.string.config_icon_mask. */ private static Path sMask; @@ -111,10 +114,9 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback * Scaled mask based on the view bounds. */ private final Path mMask; - private final Path mMaskTransformed; + private final Path mMaskScaleOnly; private final Matrix mMaskMatrix; private final Region mTransparentRegion; - private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); /** * Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and @@ -129,10 +131,19 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback */ LayerState mLayerState; + private Shader mLayersShader; + private Bitmap mLayersBitmap; + private final Rect mTmpOutRect = new Rect(); private Rect mHotspotBounds; private boolean mMutated; + private boolean mSuspendChildInvalidation; + private boolean mChildRequestedInvalidation; + private final Canvas mCanvas; + private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | + Paint.FILTER_BITMAP_FLAG); + /** * Constructor used for xml inflation. */ @@ -146,16 +157,19 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback */ AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) { mLayerState = createConstantState(state, res); - // config_icon_mask from context bound resource may have been changed using + // config_icon_mask from context bound resource may have been chaged using // OverlayManager. Read that one first. Resources r = ActivityThread.currentActivityThread() == null ? Resources.getSystem() : ActivityThread.currentActivityThread().getApplication().getResources(); - mMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask)); - mMaskTransformed = new Path(); + // TODO: either make sMask update only when config_icon_mask changes OR + // get rid of it all-together in layoutlib + sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask)); + mMask = new Path(sMask); + mMaskScaleOnly = new Path(mMask); mMaskMatrix = new Matrix(); + mCanvas = new Canvas(); mTransparentRegion = new Region(); - mPaint.setColor(Color.BLACK); } private ChildDrawable createChildDrawable(Drawable drawable) { @@ -266,7 +280,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback * @return the mask path object used to clip the drawable */ public Path getIconMask() { - return mMaskTransformed; + return mMask; } /** @@ -308,47 +322,92 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback if (bounds.isEmpty()) { return; } - // Set the child layer bounds bigger than the view port size - // by {@link #DEFAULT_VIEW_PORT_SCALE} - float cX = bounds.exactCenterX(); - float cY = bounds.exactCenterY(); - float insetWidth = bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2); - float insetHeight = bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2); - final Rect outRect = mTmpOutRect; - outRect.set( - (int) (cX - insetWidth), - (int) (cY - insetHeight), - (int) (cX + insetWidth), - (int) (cY + insetHeight)); + updateLayerBounds(bounds); + } + + private void updateLayerBounds(Rect bounds) { + if (bounds.isEmpty()) { + return; + } + try { + suspendChildInvalidation(); + updateLayerBoundsInternal(bounds); + updateMaskBoundsInternal(bounds); + } finally { + resumeChildInvalidation(); + } + } + + /** + * Set the child layer bounds bigger than the view port size by {@link #DEFAULT_VIEW_PORT_SCALE} + */ + private void updateLayerBoundsInternal(Rect bounds) { + int cX = bounds.width() / 2; + int cY = bounds.height() / 2; for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) { final ChildDrawable r = mLayerState.mChildren[i]; - if (r.mDrawable != null) { - r.mDrawable.setBounds(outRect); + final Drawable d = r.mDrawable; + if (d == null) { + continue; } + + int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2)); + int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2)); + final Rect outRect = mTmpOutRect; + outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight); + + d.setBounds(outRect); + } + } + + private void updateMaskBoundsInternal(Rect b) { + // reset everything that depends on the view bounds + mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE); + sMask.transform(mMaskMatrix, mMaskScaleOnly); + + mMaskMatrix.postTranslate(b.left, b.top); + sMask.transform(mMaskMatrix, mMask); + + if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width() + || mLayersBitmap.getHeight() != b.height()) { + mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888); } - // Update the clipping mask - mMaskMatrix.setScale(bounds.width() / MASK_SIZE, bounds.height() / MASK_SIZE); - mMaskMatrix.postTranslate(bounds.left, bounds.top); - mMask.transform(mMaskMatrix, mMaskTransformed); - - // Clear the transparent region, it is calculated lazily + mPaint.setShader(null); mTransparentRegion.setEmpty(); + mLayersShader = null; } @Override public void draw(Canvas canvas) { - int saveCount = canvas.save(); - canvas.clipPath(mMaskTransformed); - canvas.drawPaint(mPaint); - if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) { - mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(canvas); + if (mLayersBitmap == null) { + return; } - if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) { - mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(canvas); + if (mLayersShader == null) { + mCanvas.setBitmap(mLayersBitmap); + mCanvas.drawColor(Color.BLACK); + if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) { + mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(mCanvas); + } + if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) { + mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(mCanvas); + } + mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP); + mPaint.setShader(mLayersShader); } - canvas.restoreToCount(saveCount); + if (mMaskScaleOnly != null) { + Rect bounds = getBounds(); + canvas.translate(bounds.left, bounds.top); + canvas.drawPath(mMaskScaleOnly, mPaint); + canvas.translate(-bounds.left, -bounds.top); + } + } + + @Override + public void invalidateSelf() { + mLayersShader = null; + super.invalidateSelf(); } @Override @@ -541,9 +600,37 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback return false; } + /** + * Temporarily suspends child invalidation. + * + * @see #resumeChildInvalidation() + */ + private void suspendChildInvalidation() { + mSuspendChildInvalidation = true; + } + + /** + * Resumes child invalidation after suspension, immediately performing an + * invalidation if one was requested by a child during suspension. + * + * @see #suspendChildInvalidation() + */ + private void resumeChildInvalidation() { + mSuspendChildInvalidation = false; + + if (mChildRequestedInvalidation) { + mChildRequestedInvalidation = false; + invalidateSelf(); + } + } + @Override public void invalidateDrawable(@NonNull Drawable who) { - invalidateSelf(); + if (mSuspendChildInvalidation) { + mChildRequestedInvalidation = true; + } else { + invalidateSelf(); + } } @Override @@ -627,13 +714,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); - final ChildDrawable[] array = mLayerState.mChildren; - for (int i = 0; i < mLayerState.N_CHILDREN; i++) { - final Drawable dr = array[i].mDrawable; - if (dr != null) { - dr.setAlpha(alpha); - } - } } @Override @@ -736,6 +816,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback } } + if (changed) { + updateLayerBounds(getBounds()); + } + return changed; } @@ -751,6 +835,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback } } + if (changed) { + updateLayerBounds(getBounds()); + } + return changed; } @@ -891,7 +979,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback int mDensity; // The density to use when inflating/looking up the children drawables. A value of 0 means - // use the system's density. int mSrcDensityOverride = 0;