Loading res/drawable-v26/adaptive_icon_drawable_wrapper.xml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -15,7 +15,7 @@ limitations under the License. limitations under the License. --> --> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <background android:color="#FFE0E0E0"/> <background android:drawable="@color/legacy_icon_background"/> <foreground> <foreground> <com.android.launcher3.graphics.FixedScaleDrawable /> <com.android.launcher3.graphics.FixedScaleDrawable /> </foreground> </foreground> Loading res/values/colors.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -45,5 +45,6 @@ <!-- System shortcuts --> <!-- System shortcuts --> <color name="system_shortcuts_icon_color">@android:color/tertiary_text_light</color> <color name="system_shortcuts_icon_color">@android:color/tertiary_text_light</color> <color name="legacy_icon_background">#FFFFFF</color> <color name="icon_background">#E0E0E0</color> <!-- Gray 300 --> <color name="icon_background">#E0E0E0</color> <!-- Gray 300 --> </resources> </resources> src/com/android/launcher3/IconCache.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -767,7 +767,7 @@ public class IconCache { } } private static final class IconDB extends SQLiteCacheHelper { private static final class IconDB extends SQLiteCacheHelper { private final static int DB_VERSION = 11; private final static int DB_VERSION = 12; private final static int RELEASE_VERSION = DB_VERSION + private final static int RELEASE_VERSION = DB_VERSION + (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1); (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1); Loading src/com/android/launcher3/graphics/FixedScaleDrawable.java +7 −1 Original line number Original line Diff line number Diff line Loading @@ -19,15 +19,17 @@ public class FixedScaleDrawable extends DrawableWrapper { // TODO b/33553066 use the constant defined in MaskableIconDrawable // TODO b/33553066 use the constant defined in MaskableIconDrawable private static final float LEGACY_ICON_SCALE = .7f * .6667f; private static final float LEGACY_ICON_SCALE = .7f * .6667f; private float mScale; public FixedScaleDrawable() { public FixedScaleDrawable() { super(new ColorDrawable()); super(new ColorDrawable()); mScale = LEGACY_ICON_SCALE; } } @Override @Override public void draw(Canvas canvas) { public void draw(Canvas canvas) { int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.scale(LEGACY_ICON_SCALE, LEGACY_ICON_SCALE, canvas.scale(mScale, mScale, getBounds().exactCenterX(), getBounds().exactCenterY()); getBounds().exactCenterX(), getBounds().exactCenterY()); super.draw(canvas); super.draw(canvas); canvas.restoreToCount(saveCount); canvas.restoreToCount(saveCount); Loading @@ -38,4 +40,8 @@ public class FixedScaleDrawable extends DrawableWrapper { @Override @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { } public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { } public void setScale(float scale) { mScale = scale * LEGACY_ICON_SCALE; } } } src/com/android/launcher3/graphics/IconNormalizer.java +173 −8 Original line number Original line Diff line number Diff line Loading @@ -20,15 +20,31 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Canvas; import android.graphics.Color; 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.Rect; import android.graphics.RectF; import android.graphics.RectF; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; import java.io.File; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.ByteBuffer; import java.util.Random; public class IconNormalizer { public class IconNormalizer { private static final String TAG = "IconNormalizer"; private static final boolean DEBUG = false; // Ratio of icon visible area to full icon size for a square shaped icon // Ratio of icon visible area to full icon size for a square shaped icon private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576; private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576; // Ratio of icon visible area to full icon size for a circular shaped icon // Ratio of icon visible area to full icon size for a circular shaped icon Loading @@ -42,17 +58,36 @@ public class IconNormalizer { private static final int MIN_VISIBLE_ALPHA = 40; private static final int MIN_VISIBLE_ALPHA = 40; // Shape detection related constants private static final float BOUND_RATIO_MARGIN = .05f; private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f; private static final float SCALE_NOT_INITIALIZED = 0; private static final Object LOCK = new Object(); private static final Object LOCK = new Object(); private static IconNormalizer sIconNormalizer; private static IconNormalizer sIconNormalizer; 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 mPaintMaskShapeOutline; private final byte[] mPixels; private final byte[] mPixels; private final int[] mPixelsARGB; private float mAdaptiveIconScale; // for each y, stores the position of the leftmost x and the rightmost x // for each y, stores the position of the leftmost x and the rightmost x private final float[] mLeftBorder; private final float[] mLeftBorder; private final float[] mRightBorder; private final float[] mRightBorder; private final Rect mBounds; private final Matrix mMatrix; private Paint mPaintIcon; private Canvas mCanvasARGB; private File mDir; private int mFileId; private Random mRandom; private IconNormalizer(Context context) { private 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. Loading @@ -60,9 +95,121 @@ 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(); // 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.setColor(Color.RED); mPaintMaskShape.setStyle(Paint.Style.FILL); mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); mPaintMaskShapeOutline = new Paint(); mPaintMaskShapeOutline.setStrokeWidth(2 * context.getResources().getDisplayMetrics().density); mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE); mPaintMaskShapeOutline.setColor(Color.BLACK); mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); mMatrix = new Matrix(); int[] mPixels = new int[mMaxSize * mMaxSize]; mAdaptiveIconScale = SCALE_NOT_INITIALIZED; mDir = context.getExternalFilesDir(null); mRandom = new Random(); } /** * Returns if the shape of the icon is same as the path. * For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds. */ private boolean isShape(Path maskPath) { // Condition1: // If width and height of the path not close to a square, then the icon shape is // not same as the mask shape. float iconRatio = ((float) mBounds.width()) / mBounds.height(); if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) { if (DEBUG) { Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio); } return false; } // Condition 2: // 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. mFileId = mRandom.nextInt(); mBitmapARGB.eraseColor(Color.TRANSPARENT); mCanvasARGB.drawBitmap(mBitmap, 0, 0, mPaintIcon); if (DEBUG) { final File beforeFile = new File(mDir, "isShape" + mFileId + "_before.png"); try { mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(beforeFile)); } catch (Exception e) {} } // Fit the shape within the icon's bounding box mMatrix.reset(); mMatrix.setScale(mBounds.width(), mBounds.height()); mMatrix.postTranslate(mBounds.left, mBounds.top); maskPath.transform(mMatrix); // XOR operation mCanvasARGB.drawPath(maskPath, mPaintMaskShape); // DST_OUT operation around the mask path outline mCanvasARGB.drawPath(maskPath, mPaintMaskShapeOutline); boolean isTrans = isTransparentBitmap(mBitmapARGB); if (DEBUG) { final File afterFile = new File(mDir, "isShape" + mFileId + "_after_" + isTrans + ".png"); try { mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(afterFile)); } catch (Exception e) {} } // Check if the result is almost transparent if (!isTrans) { if (DEBUG) { Log.d(TAG, "Not same as mask shape"); } return false; } return true; } /** * Used to determine if certain the bitmap is transparent. */ private boolean isTransparentBitmap(Bitmap bitmap) { int w = mBounds.width(); int h = mBounds.height(); bitmap.getPixels(mPixelsARGB, 0 /* the first index to write into the array */, w /* stride */, mBounds.left, mBounds.top, w, h); int sum = 0; for (int i = 0; i < w * h; i++) { if(Color.alpha(mPixelsARGB[i]) > MIN_VISIBLE_ALPHA) { sum++; } } float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height()); boolean transparentImage = percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD; if (DEBUG) { Log.d(TAG, "Total # pixel that is different (id="+ mFileId + "):" + percentageDiffPixels + "="+ sum + "/" + mBounds.width() * mBounds.height()); } return transparentImage; } } /** /** Loading @@ -79,7 +226,15 @@ public class IconNormalizer { * * * @param outBounds optional rect to receive the fraction distance from each edge. * @param outBounds optional rect to receive the fraction distance from each edge. */ */ public synchronized float getScale(Drawable d, RectF outBounds) { public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds, @Nullable Path path, @Nullable boolean[] outMaskShape) { if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable && mAdaptiveIconScale != SCALE_NOT_INITIALIZED) { if (outBounds != null) { outBounds.set(mBounds); } return mAdaptiveIconScale; } int width = d.getIntrinsicWidth(); int width = d.getIntrinsicWidth(); int height = d.getIntrinsicHeight(); int height = d.getIntrinsicHeight(); if (width <= 0 || height <= 0) { if (width <= 0 || height <= 0) { Loading Loading @@ -171,18 +326,28 @@ public class IconNormalizer { } else { } else { scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); } } mBounds.left = leftX; mBounds.right = rightX; if (outBounds != null) { mBounds.top = topY; outBounds.left = ((float) leftX) / width; mBounds.bottom = bottomY; outBounds.right = 1 - ((float) rightX) / width; outBounds.top = ((float) topY) / height; if (outBounds != null) { outBounds.bottom = 1 - ((float) bottomY) / height; outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top), 1 - ((float) mBounds.right) / width, 1 - ((float) mBounds.bottom) / height); } } if (outMaskShape != null && outMaskShape.length > 0) { outMaskShape[0] = isShape(path); } float areaScale = area / (width * height); float areaScale = area / (width * height); // Use sqrt of the final ratio as the images is scaled across both width and height. // Use sqrt of the final ratio as the images is scaled across both width and height. float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable && mAdaptiveIconScale == SCALE_NOT_INITIALIZED) { mAdaptiveIconScale = scale; } return scale; return scale; } } Loading Loading
res/drawable-v26/adaptive_icon_drawable_wrapper.xml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -15,7 +15,7 @@ limitations under the License. limitations under the License. --> --> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <background android:color="#FFE0E0E0"/> <background android:drawable="@color/legacy_icon_background"/> <foreground> <foreground> <com.android.launcher3.graphics.FixedScaleDrawable /> <com.android.launcher3.graphics.FixedScaleDrawable /> </foreground> </foreground> Loading
res/values/colors.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -45,5 +45,6 @@ <!-- System shortcuts --> <!-- System shortcuts --> <color name="system_shortcuts_icon_color">@android:color/tertiary_text_light</color> <color name="system_shortcuts_icon_color">@android:color/tertiary_text_light</color> <color name="legacy_icon_background">#FFFFFF</color> <color name="icon_background">#E0E0E0</color> <!-- Gray 300 --> <color name="icon_background">#E0E0E0</color> <!-- Gray 300 --> </resources> </resources>
src/com/android/launcher3/IconCache.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -767,7 +767,7 @@ public class IconCache { } } private static final class IconDB extends SQLiteCacheHelper { private static final class IconDB extends SQLiteCacheHelper { private final static int DB_VERSION = 11; private final static int DB_VERSION = 12; private final static int RELEASE_VERSION = DB_VERSION + private final static int RELEASE_VERSION = DB_VERSION + (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1); (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1); Loading
src/com/android/launcher3/graphics/FixedScaleDrawable.java +7 −1 Original line number Original line Diff line number Diff line Loading @@ -19,15 +19,17 @@ public class FixedScaleDrawable extends DrawableWrapper { // TODO b/33553066 use the constant defined in MaskableIconDrawable // TODO b/33553066 use the constant defined in MaskableIconDrawable private static final float LEGACY_ICON_SCALE = .7f * .6667f; private static final float LEGACY_ICON_SCALE = .7f * .6667f; private float mScale; public FixedScaleDrawable() { public FixedScaleDrawable() { super(new ColorDrawable()); super(new ColorDrawable()); mScale = LEGACY_ICON_SCALE; } } @Override @Override public void draw(Canvas canvas) { public void draw(Canvas canvas) { int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.scale(LEGACY_ICON_SCALE, LEGACY_ICON_SCALE, canvas.scale(mScale, mScale, getBounds().exactCenterX(), getBounds().exactCenterY()); getBounds().exactCenterX(), getBounds().exactCenterY()); super.draw(canvas); super.draw(canvas); canvas.restoreToCount(saveCount); canvas.restoreToCount(saveCount); Loading @@ -38,4 +40,8 @@ public class FixedScaleDrawable extends DrawableWrapper { @Override @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { } public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { } public void setScale(float scale) { mScale = scale * LEGACY_ICON_SCALE; } } }
src/com/android/launcher3/graphics/IconNormalizer.java +173 −8 Original line number Original line Diff line number Diff line Loading @@ -20,15 +20,31 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Canvas; import android.graphics.Color; 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.Rect; import android.graphics.RectF; import android.graphics.RectF; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; import java.io.File; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.ByteBuffer; import java.util.Random; public class IconNormalizer { public class IconNormalizer { private static final String TAG = "IconNormalizer"; private static final boolean DEBUG = false; // Ratio of icon visible area to full icon size for a square shaped icon // Ratio of icon visible area to full icon size for a square shaped icon private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576; private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576; // Ratio of icon visible area to full icon size for a circular shaped icon // Ratio of icon visible area to full icon size for a circular shaped icon Loading @@ -42,17 +58,36 @@ public class IconNormalizer { private static final int MIN_VISIBLE_ALPHA = 40; private static final int MIN_VISIBLE_ALPHA = 40; // Shape detection related constants private static final float BOUND_RATIO_MARGIN = .05f; private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f; private static final float SCALE_NOT_INITIALIZED = 0; private static final Object LOCK = new Object(); private static final Object LOCK = new Object(); private static IconNormalizer sIconNormalizer; private static IconNormalizer sIconNormalizer; 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 mPaintMaskShapeOutline; private final byte[] mPixels; private final byte[] mPixels; private final int[] mPixelsARGB; private float mAdaptiveIconScale; // for each y, stores the position of the leftmost x and the rightmost x // for each y, stores the position of the leftmost x and the rightmost x private final float[] mLeftBorder; private final float[] mLeftBorder; private final float[] mRightBorder; private final float[] mRightBorder; private final Rect mBounds; private final Matrix mMatrix; private Paint mPaintIcon; private Canvas mCanvasARGB; private File mDir; private int mFileId; private Random mRandom; private IconNormalizer(Context context) { private 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. Loading @@ -60,9 +95,121 @@ 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(); // 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.setColor(Color.RED); mPaintMaskShape.setStyle(Paint.Style.FILL); mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); mPaintMaskShapeOutline = new Paint(); mPaintMaskShapeOutline.setStrokeWidth(2 * context.getResources().getDisplayMetrics().density); mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE); mPaintMaskShapeOutline.setColor(Color.BLACK); mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); mMatrix = new Matrix(); int[] mPixels = new int[mMaxSize * mMaxSize]; mAdaptiveIconScale = SCALE_NOT_INITIALIZED; mDir = context.getExternalFilesDir(null); mRandom = new Random(); } /** * Returns if the shape of the icon is same as the path. * For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds. */ private boolean isShape(Path maskPath) { // Condition1: // If width and height of the path not close to a square, then the icon shape is // not same as the mask shape. float iconRatio = ((float) mBounds.width()) / mBounds.height(); if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) { if (DEBUG) { Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio); } return false; } // Condition 2: // 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. mFileId = mRandom.nextInt(); mBitmapARGB.eraseColor(Color.TRANSPARENT); mCanvasARGB.drawBitmap(mBitmap, 0, 0, mPaintIcon); if (DEBUG) { final File beforeFile = new File(mDir, "isShape" + mFileId + "_before.png"); try { mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(beforeFile)); } catch (Exception e) {} } // Fit the shape within the icon's bounding box mMatrix.reset(); mMatrix.setScale(mBounds.width(), mBounds.height()); mMatrix.postTranslate(mBounds.left, mBounds.top); maskPath.transform(mMatrix); // XOR operation mCanvasARGB.drawPath(maskPath, mPaintMaskShape); // DST_OUT operation around the mask path outline mCanvasARGB.drawPath(maskPath, mPaintMaskShapeOutline); boolean isTrans = isTransparentBitmap(mBitmapARGB); if (DEBUG) { final File afterFile = new File(mDir, "isShape" + mFileId + "_after_" + isTrans + ".png"); try { mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(afterFile)); } catch (Exception e) {} } // Check if the result is almost transparent if (!isTrans) { if (DEBUG) { Log.d(TAG, "Not same as mask shape"); } return false; } return true; } /** * Used to determine if certain the bitmap is transparent. */ private boolean isTransparentBitmap(Bitmap bitmap) { int w = mBounds.width(); int h = mBounds.height(); bitmap.getPixels(mPixelsARGB, 0 /* the first index to write into the array */, w /* stride */, mBounds.left, mBounds.top, w, h); int sum = 0; for (int i = 0; i < w * h; i++) { if(Color.alpha(mPixelsARGB[i]) > MIN_VISIBLE_ALPHA) { sum++; } } float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height()); boolean transparentImage = percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD; if (DEBUG) { Log.d(TAG, "Total # pixel that is different (id="+ mFileId + "):" + percentageDiffPixels + "="+ sum + "/" + mBounds.width() * mBounds.height()); } return transparentImage; } } /** /** Loading @@ -79,7 +226,15 @@ public class IconNormalizer { * * * @param outBounds optional rect to receive the fraction distance from each edge. * @param outBounds optional rect to receive the fraction distance from each edge. */ */ public synchronized float getScale(Drawable d, RectF outBounds) { public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds, @Nullable Path path, @Nullable boolean[] outMaskShape) { if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable && mAdaptiveIconScale != SCALE_NOT_INITIALIZED) { if (outBounds != null) { outBounds.set(mBounds); } return mAdaptiveIconScale; } int width = d.getIntrinsicWidth(); int width = d.getIntrinsicWidth(); int height = d.getIntrinsicHeight(); int height = d.getIntrinsicHeight(); if (width <= 0 || height <= 0) { if (width <= 0 || height <= 0) { Loading Loading @@ -171,18 +326,28 @@ public class IconNormalizer { } else { } else { scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); } } mBounds.left = leftX; mBounds.right = rightX; if (outBounds != null) { mBounds.top = topY; outBounds.left = ((float) leftX) / width; mBounds.bottom = bottomY; outBounds.right = 1 - ((float) rightX) / width; outBounds.top = ((float) topY) / height; if (outBounds != null) { outBounds.bottom = 1 - ((float) bottomY) / height; outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top), 1 - ((float) mBounds.right) / width, 1 - ((float) mBounds.bottom) / height); } } if (outMaskShape != null && outMaskShape.length > 0) { outMaskShape[0] = isShape(path); } float areaScale = area / (width * height); float areaScale = area / (width * height); // Use sqrt of the final ratio as the images is scaled across both width and height. // Use sqrt of the final ratio as the images is scaled across both width and height. float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable && mAdaptiveIconScale == SCALE_NOT_INITIALIZED) { mAdaptiveIconScale = scale; } return scale; return scale; } } Loading