summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2017-02-19 20:21:37 -0800
committerSunny Goyal <sunnygoyal@google.com>2017-03-03 12:51:26 -0800
commit19b93b7988c12a25d9a88ca808d871916263ef9f (patch)
treea82ed769d61b543100be2e162da149c63d973b74 /src/com/android
parent4ff3457e77915b596cfa896a4f98283ace2314de (diff)
downloadpackages_apps_Trebuchet-19b93b7988c12a25d9a88ca808d871916263ef9f.tar.gz
packages_apps_Trebuchet-19b93b7988c12a25d9a88ca808d871916263ef9f.tar.bz2
packages_apps_Trebuchet-19b93b7988c12a25d9a88ca808d871916263ef9f.zip
Updating FolderIcon drawing to avoid dependency on software layer.
Instead of cliping using clipPath, using PorterDuffXfermode to clear area outside the bounds. This provides much better support for antialiasing. Software layer was also being used for drawing shadow. But since the paint color was set to transparent, no shadow was being drawn in O. Instead drawing the shadow using radial gradient. Bug: 35680116 Bug: 35428783 Change-Id: I0431664400628d5d01675fe72fe7ade01b64d611
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/launcher3/CellLayout.java9
-rw-r--r--src/com/android/launcher3/folder/FolderIcon.java200
2 files changed, 132 insertions, 77 deletions
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index e0fcbf04d..8179dad29 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -105,7 +105,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
private ArrayList<FolderIcon.PreviewBackground> mFolderBackgrounds = new ArrayList<FolderIcon.PreviewBackground>();
FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
- Paint mFolderBgPaint = new Paint();
private float mBackgroundAlpha;
@@ -501,9 +500,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
canvas.save();
canvas.translate(mTempLocation[0], mTempLocation[1]);
- bg.drawBackground(canvas, mFolderBgPaint);
+ bg.drawBackground(canvas);
if (!bg.isClipping) {
- bg.drawBackgroundStroke(canvas, mFolderBgPaint);
+ bg.drawBackgroundStroke(canvas);
}
canvas.restore();
}
@@ -513,7 +512,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mFolderLeaveBehind.delegateCellY, mTempLocation);
canvas.save();
canvas.translate(mTempLocation[0], mTempLocation[1]);
- mFolderLeaveBehind.drawLeaveBehind(canvas, mFolderBgPaint);
+ mFolderLeaveBehind.drawLeaveBehind(canvas);
canvas.restore();
}
}
@@ -528,7 +527,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
canvas.save();
canvas.translate(mTempLocation[0], mTempLocation[1]);
- bg.drawBackgroundStroke(canvas, mFolderBgPaint);
+ bg.drawBackgroundStroke(canvas);
canvas.restore();
}
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 969798530..f21601092 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -24,11 +24,15 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -125,8 +129,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
private Drawable mReferenceDrawable = null;
- Paint mBgPaint = new Paint();
-
private Alarm mOpenAlarm = new Alarm();
private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
@@ -179,11 +181,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
DeviceProfile grid = launcher.getDeviceProfile();
FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
- // For performance and compatibility reasons we render the preview using a software layer.
- // In particular, hardware path clipping has spotty ecosystem support and bad performance.
- // Software rendering also allows us to use shadow layers.
- icon.setLayerType(LAYER_TYPE_SOFTWARE, new Paint(Paint.FILTER_BITMAP_FLAG));
-
icon.setClipToPadding(false);
icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
icon.mFolderName.setText(folderInfo.title);
@@ -493,7 +490,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
- canvas.save();
+ canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(params.transX, params.transY);
canvas.scale(params.scale, params.scale);
Drawable d = params.drawable;
@@ -520,10 +517,29 @@ public class FolderIcon extends FrameLayout implements FolderListener {
* information, handles drawing, and animation (accept state <--> rest state).
*/
public static class PreviewBackground {
+
+ private final PorterDuffXfermode mClipPorterDuffXfermode
+ = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
+ // Create a RadialGradient such that it draws a black circle and then extends with
+ // transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
+ // just at the edge quickly change it to transparent.
+ private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
+ new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
+ new float[] {0, 0.999f, 1},
+ Shader.TileMode.CLAMP);
+
+ private final PorterDuffXfermode mShadowPorterDuffXfermode
+ = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
+ private RadialGradient mShadowShader = null;
+
+ private final Matrix mShaderMatrix = new Matrix();
+ private final Path mPath = new Path();
+
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
private float mScale = 1f;
private float mColorMultiplier = 1f;
- private Path mClipPath = new Path();
- private int mStrokeWidth;
+ private float mStrokeWidth;
private View mInvalidateDelegate;
public int previewSize;
@@ -546,7 +562,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
private static final int BG_OPACITY = 160;
private static final int MAX_BG_OPACITY = 225;
private static final int BG_INTENSITY = 245;
- private static final int SHADOW_OPACITY = 80;
+ private static final int SHADOW_OPACITY = 40;
ValueAnimator mScaleAnimator;
@@ -562,7 +578,16 @@ public class FolderIcon extends FrameLayout implements FolderListener {
basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
- mStrokeWidth = Utilities.pxFromDp(1, dm);
+ // Stroke width is 1dp
+ mStrokeWidth = dm.density;
+
+ float radius = getScaledRadius();
+ float shadowRadius = radius + mStrokeWidth;
+ int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
+ mShadowShader = new RadialGradient(0, 0, 1,
+ new int[] {shadowColor, Color.TRANSPARENT},
+ new float[] {radius / shadowRadius, 1},
+ Shader.TileMode.CLAMP);
invalidate();
}
@@ -592,10 +617,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
void invalidate() {
- int radius = getScaledRadius();
- mClipPath.reset();
- mClipPath.addCircle(radius, radius, radius, Path.Direction.CW);
-
if (mInvalidateDelegate != null) {
mInvalidateDelegate.invalidate();
}
@@ -610,70 +631,94 @@ public class FolderIcon extends FrameLayout implements FolderListener {
invalidate();
}
- public void drawBackground(Canvas canvas, Paint paint) {
- canvas.save();
- canvas.translate(getOffsetX(), getOffsetY());
-
- paint.reset();
- paint.setStyle(Paint.Style.FILL);
- paint.setXfermode(null);
- paint.setAntiAlias(true);
-
+ public void drawBackground(Canvas canvas) {
+ mPaint.setStyle(Paint.Style.FILL);
int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
- paint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
+ mPaint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
+
+ drawCircle(canvas, 0 /* deltaRadius */);
+ // Draw shadow.
+ if (mShadowShader == null) {
+ return;
+ }
float radius = getScaledRadius();
+ float shadowRadius = radius + mStrokeWidth;
+ mPaint.setColor(Color.BLACK);
+ int offsetX = getOffsetX();
+ int offsetY = getOffsetY();
+ final int saveCount;
+ if (canvas.isHardwareAccelerated()) {
+ saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
+ offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
+ null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
- canvas.drawCircle(radius, radius, radius, paint);
- canvas.clipPath(mClipPath, Region.Op.DIFFERENCE);
+ } else {
+ saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
+ }
- paint.setStyle(Paint.Style.STROKE);
- paint.setColor(Color.TRANSPARENT);
- paint.setShadowLayer(mStrokeWidth, 0, mStrokeWidth, Color.argb(SHADOW_OPACITY, 0, 0, 0));
- canvas.drawCircle(radius, radius, radius, paint);
+ mShaderMatrix.setScale(shadowRadius, shadowRadius);
+ mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
+ mShadowShader.setLocalMatrix(mShaderMatrix);
+ mPaint.setShader(mShadowShader);
+ canvas.drawPaint(mPaint);
+ mPaint.setShader(null);
+
+ if (canvas.isHardwareAccelerated()) {
+ mPaint.setXfermode(mShadowPorterDuffXfermode);
+ canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
+ mPaint.setXfermode(null);
+ }
- canvas.restore();
+ canvas.restoreToCount(saveCount);
}
- public void drawBackgroundStroke(Canvas canvas, Paint paint) {
- canvas.save();
- canvas.translate(getOffsetX(), getOffsetY());
-
- paint.reset();
- paint.setAntiAlias(true);
- paint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
- paint.setStyle(Paint.Style.STROKE);
- paint.setStrokeWidth(mStrokeWidth);
-
- float radius = getScaledRadius();
- canvas.drawCircle(radius, radius, radius - 1, paint);
-
- canvas.restore();
+ public void drawBackgroundStroke(Canvas canvas) {
+ mPaint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(mStrokeWidth);
+ drawCircle(canvas, 1 /* deltaRadius */);
}
- public void drawLeaveBehind(Canvas canvas, Paint paint) {
+ public void drawLeaveBehind(Canvas canvas) {
float originalScale = mScale;
mScale = 0.5f;
- canvas.save();
- canvas.translate(getOffsetX(), getOffsetY());
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(Color.argb(160, 245, 245, 245));
+ drawCircle(canvas, 0 /* deltaRadius */);
- paint.reset();
- paint.setAntiAlias(true);
- paint.setColor(Color.argb(160, 245, 245, 245));
+ mScale = originalScale;
+ }
+ private void drawCircle(Canvas canvas,float deltaRadius) {
float radius = getScaledRadius();
- canvas.drawCircle(radius, radius, radius, paint);
+ canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
+ radius - deltaRadius, mPaint);
+ }
- canvas.restore();
- mScale = originalScale;
+ // It is the callers responsibility to save and restore the canvas layers.
+ private void clipCanvasSoftware(Canvas canvas, Region.Op op) {
+ mPath.reset();
+ float r = getScaledRadius();
+ mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
+ canvas.clipPath(mPath, op);
}
- // It is the callers responsibility to save and restore the canvas.
- private void clipCanvas(Canvas canvas) {
- canvas.translate(getOffsetX(), getOffsetY());
- canvas.clipPath(mClipPath);
- canvas.translate(-getOffsetX(), -getOffsetY());
+ // It is the callers responsibility to save and restore the canvas layers.
+ private void clipCanvasHardware(Canvas canvas) {
+ mPaint.setColor(Color.BLACK);
+ mPaint.setXfermode(mClipPorterDuffXfermode);
+
+ float radius = getScaledRadius();
+ mShaderMatrix.setScale(radius, radius);
+ mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
+ mClipShader.setLocalMatrix(mShaderMatrix);
+ mPaint.setShader(mClipShader);
+ canvas.drawPaint(mPaint);
+ mPaint.setXfermode(null);
+ mPaint.setShader(null);
}
private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
@@ -793,17 +838,22 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
if (!mBackground.drawingDelegated()) {
- mBackground.drawBackground(canvas, mBgPaint);
+ mBackground.drawBackground(canvas);
}
if (mFolder == null) return;
if (mFolder.getItemCount() == 0 && !mAnimating) return;
- canvas.save();
-
+ final int saveCount;
- if (mPreviewLayoutRule.clipToBackground()) {
- mBackground.clipCanvas(canvas);
+ if (canvas.isHardwareAccelerated()) {
+ saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
+ Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ } else {
+ saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ if (mPreviewLayoutRule.clipToBackground()) {
+ mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT);
+ }
}
// The items are drawn in coordinates relative to the preview offset
@@ -816,18 +866,24 @@ public class FolderIcon extends FrameLayout implements FolderListener {
drawPreviewItem(canvas, p);
}
}
- canvas.restore();
+ canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY);
+
+ if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
+ mBackground.clipCanvasHardware(canvas);
+ }
+ canvas.restoreToCount(saveCount);
if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) {
- mBackground.drawBackgroundStroke(canvas, mBgPaint);
+ mBackground.drawBackgroundStroke(canvas);
}
- int offsetX = mBackground.getOffsetX();
- int offsetY = mBackground.getOffsetY();
- int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
- Rect bounds = new Rect(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
if ((mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0) {
// If we are animating to the accepting state, animate the badge out.
+ int offsetX = mBackground.getOffsetX();
+ int offsetY = mBackground.getOffsetY();
+ int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
+ Rect bounds = new Rect(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
+
float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
mBadgeRenderer.draw(canvas, IconPalette.FOLDER_ICON_PALETTE, mBadgeInfo, bounds, badgeScale);
}