Reusing the same bitmap and pixel array for shape detection

Bug: 76162466
Change-Id: I785d9b8673b2b8b0a56a146143b375c412e9322a
This commit is contained in:
Sunny Goyal 2018-04-30 09:20:58 -07:00
parent c2c907edec
commit 2e4596a448
2 changed files with 26 additions and 30 deletions

View File

@ -780,7 +780,7 @@ public class IconCache {
} }
private static final class IconDB extends SQLiteCacheHelper { private static final class IconDB extends SQLiteCacheHelper {
private final static int RELEASE_VERSION = 21; private final static int RELEASE_VERSION = 22;
private final static String TABLE_NAME = "icons"; private final static String TABLE_NAME = "icons";
private final static String COLUMN_ROWID = "rowid"; private final static String COLUMN_ROWID = "rowid";

View File

@ -24,6 +24,7 @@ import android.graphics.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode; import android.graphics.PorterDuffXfermode;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
@ -66,12 +67,10 @@ public class IconNormalizer {
private final int mMaxSize; private final int mMaxSize;
private final Bitmap mBitmap; private final Bitmap mBitmap;
private final Bitmap mBitmapARGB;
private final Canvas mCanvas; private final Canvas mCanvas;
private final Paint mPaintMaskShape; private final Paint mPaintMaskShape;
private final Paint mPaintMaskShapeOutline; private final Paint mPaintMaskShapeOutline;
private final byte[] mPixels; private final byte[] mPixels;
private final int[] mPixelsARGB;
private final Rect mAdaptiveIconBounds; private final Rect mAdaptiveIconBounds;
private float mAdaptiveIconScale; private float mAdaptiveIconScale;
@ -83,9 +82,6 @@ public class IconNormalizer {
private final Path mShapePath; private final Path mShapePath;
private final Matrix mMatrix; private final Matrix mMatrix;
private final Paint mPaintIcon;
private final Canvas mCanvasARGB;
/** package private **/ /** package private **/
IconNormalizer(Context context) { IconNormalizer(Context context) {
// Use twice the icon size as maximum size to avoid scaling down twice. // Use twice the icon size as maximum size to avoid scaling down twice.
@ -93,19 +89,11 @@ public class IconNormalizer {
mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8); mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
mCanvas = new Canvas(mBitmap); mCanvas = new Canvas(mBitmap);
mPixels = new byte[mMaxSize * mMaxSize]; mPixels = new byte[mMaxSize * mMaxSize];
mPixelsARGB = new int[mMaxSize * mMaxSize];
mLeftBorder = new float[mMaxSize]; mLeftBorder = new float[mMaxSize];
mRightBorder = new float[mMaxSize]; mRightBorder = new float[mMaxSize];
mBounds = new Rect(); mBounds = new Rect();
mAdaptiveIconBounds = new Rect(); mAdaptiveIconBounds = new Rect();
// Needed for isShape() method
mBitmapARGB = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ARGB_8888);
mCanvasARGB = new Canvas(mBitmapARGB);
mPaintIcon = new Paint();
mPaintIcon.setColor(Color.WHITE);
mPaintMaskShape = new Paint(); mPaintMaskShape = new Paint();
mPaintMaskShape.setColor(Color.RED); mPaintMaskShape.setColor(Color.RED);
mPaintMaskShape.setStyle(Paint.Style.FILL); mPaintMaskShape.setStyle(Paint.Style.FILL);
@ -115,7 +103,7 @@ public class IconNormalizer {
mPaintMaskShapeOutline.setStrokeWidth(2 * context.getResources().getDisplayMetrics().density); mPaintMaskShapeOutline.setStrokeWidth(2 * context.getResources().getDisplayMetrics().density);
mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE); mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE);
mPaintMaskShapeOutline.setColor(Color.BLACK); mPaintMaskShapeOutline.setColor(Color.BLACK);
mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mShapePath = new Path(); mShapePath = new Path();
mMatrix = new Matrix(); mMatrix = new Matrix();
@ -141,8 +129,6 @@ public class IconNormalizer {
// Condition 2: // Condition 2:
// Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation // Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
// should generate transparent image, if the actual icon is equivalent to the shape. // should generate transparent image, if the actual icon is equivalent to the shape.
mBitmapARGB.eraseColor(Color.TRANSPARENT);
mCanvasARGB.drawBitmap(mBitmap, 0, 0, mPaintIcon);
// Fit the shape within the icon's bounding box // Fit the shape within the icon's bounding box
mMatrix.reset(); mMatrix.reset();
@ -151,31 +137,41 @@ public class IconNormalizer {
maskPath.transform(mMatrix, mShapePath); maskPath.transform(mMatrix, mShapePath);
// XOR operation // XOR operation
mCanvasARGB.drawPath(mShapePath, mPaintMaskShape); mCanvas.drawPath(mShapePath, mPaintMaskShape);
// DST_OUT operation around the mask path outline // DST_OUT operation around the mask path outline
mCanvasARGB.drawPath(mShapePath, mPaintMaskShapeOutline); mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline);
// Check if the result is almost transparent // Check if the result is almost transparent
return isTransparentBitmap(mBitmapARGB); return isTransparentBitmap();
} }
/** /**
* Used to determine if certain the bitmap is transparent. * Used to determine if certain the bitmap is transparent.
*/ */
private boolean isTransparentBitmap(Bitmap bitmap) { private boolean isTransparentBitmap() {
int w = mBounds.width(); ByteBuffer buffer = ByteBuffer.wrap(mPixels);
int h = mBounds.height(); buffer.rewind();
bitmap.getPixels(mPixelsARGB, 0 /* the first index to write into the array */, mBitmap.copyPixelsToBuffer(buffer);
w /* stride */,
mBounds.left, mBounds.top, int y = mBounds.top;
w, h); // buffer position
int index = y * mMaxSize;
// buffer shift after every row, width of buffer = mMaxSize
int rowSizeDiff = mMaxSize - mBounds.right;
int sum = 0; int sum = 0;
for (int i = w * h - 1; i >= 0; i--) { for (; y < mBounds.bottom; y++) {
if(Color.alpha(mPixelsARGB[i]) > MIN_VISIBLE_ALPHA) { index += mBounds.left;
for (int x = mBounds.left; x < mBounds.right; x++) {
if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
sum++; sum++;
} }
index++;
} }
index += rowSizeDiff;
}
float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height()); float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD; return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
} }
@ -306,7 +302,7 @@ public class IconNormalizer {
mBounds.bottom = bottomY; mBounds.bottom = bottomY;
if (outBounds != null) { if (outBounds != null) {
outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top), outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top) / height,
1 - ((float) mBounds.right) / width, 1 - ((float) mBounds.right) / width,
1 - ((float) mBounds.bottom) / height); 1 - ((float) mBounds.bottom) / height);
} }