Fine tune splash screen icon generation algorithm
Filter out the translucent pixels when compute the foreground colors of an adaptive icon, which should make sense because the translucent pixels on icon should only used for decoration, do not let it affect the opaque pixels, also with this filter we won't drop down the performance when using palette. Bug: 193304878 Test: manual launch single color background apps. Change-Id: Ia9873cf907d30bb7f30318a289a54159d0a51ee3
This commit is contained in:
@@ -23,6 +23,7 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLA
|
||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
|
||||
|
||||
import android.annotation.ColorInt;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -64,6 +65,7 @@ import com.android.wm.shell.common.TransactionPool;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
@@ -132,7 +134,6 @@ public class SplashscreenContentDrawer {
|
||||
* @param splashScreenViewConsumer Receiving the SplashScreenView object, which will also be
|
||||
* executed on splash screen thread. Note that the view can be
|
||||
* null if failed.
|
||||
* @param bgColorConsumer Receiving the background color once it's estimated complete.
|
||||
*/
|
||||
void createContentView(Context context, @StartingWindowType int suggestType, ActivityInfo info,
|
||||
int taskId, Consumer<SplashScreenView> splashScreenViewConsumer) {
|
||||
@@ -209,9 +210,9 @@ public class SplashscreenContentDrawer {
|
||||
}
|
||||
|
||||
private static int estimateWindowBGColor(Drawable themeBGDrawable) {
|
||||
final DrawableColorTester themeBGTester =
|
||||
new DrawableColorTester(themeBGDrawable, true /* filterTransparent */);
|
||||
if (themeBGTester.nonTransparentRatio() == 0) {
|
||||
final DrawableColorTester themeBGTester = new DrawableColorTester(
|
||||
themeBGDrawable, DrawableColorTester.TRANSPARENT_FILTER /* filterType */);
|
||||
if (themeBGTester.passFilterRatio() == 0) {
|
||||
// the window background is transparent, unable to draw
|
||||
Slog.w(TAG, "Window background is transparent, fill background with black color");
|
||||
return getSystemBGColor();
|
||||
@@ -414,7 +415,8 @@ public class SplashscreenContentDrawer {
|
||||
final ColorCache.IconColor iconColor = mColorCache.getIconColor(
|
||||
mActivityInfo.packageName, mActivityInfo.getIconResource(),
|
||||
mLastPackageContextConfigHash,
|
||||
() -> new DrawableColorTester(iconForeground, true /* filterTransparent */),
|
||||
() -> new DrawableColorTester(iconForeground,
|
||||
DrawableColorTester.TRANSLUCENT_FILTER /* filterType */),
|
||||
() -> new DrawableColorTester(adaptiveIconDrawable.getBackground()));
|
||||
|
||||
if (DEBUG) {
|
||||
@@ -443,7 +445,7 @@ public class SplashscreenContentDrawer {
|
||||
// Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
|
||||
// scale by 192/160 if we only draw adaptiveIcon's foreground.
|
||||
final float noBgScale =
|
||||
iconColor.mFgNonTransparentRatio < ENLARGE_FOREGROUND_ICON_THRESHOLD
|
||||
iconColor.mFgNonTranslucentRatio < ENLARGE_FOREGROUND_ICON_THRESHOLD
|
||||
? NO_BACKGROUND_SCALE : 1f;
|
||||
// Using AdaptiveIconDrawable here can help keep the shape consistent with the
|
||||
// current settings.
|
||||
@@ -545,13 +547,26 @@ public class SplashscreenContentDrawer {
|
||||
}
|
||||
|
||||
private static class DrawableColorTester {
|
||||
private static final int NO_ALPHA_FILTER = 0;
|
||||
// filter out completely invisible pixels
|
||||
private static final int TRANSPARENT_FILTER = 1;
|
||||
// filter out translucent and invisible pixels
|
||||
private static final int TRANSLUCENT_FILTER = 2;
|
||||
|
||||
@IntDef(flag = true, value = {
|
||||
NO_ALPHA_FILTER,
|
||||
TRANSPARENT_FILTER,
|
||||
TRANSLUCENT_FILTER
|
||||
})
|
||||
private @interface QuantizerFilterType {}
|
||||
|
||||
private final ColorTester mColorChecker;
|
||||
|
||||
DrawableColorTester(Drawable drawable) {
|
||||
this(drawable, false /* filterTransparent */);
|
||||
this(drawable, NO_ALPHA_FILTER /* filterType */);
|
||||
}
|
||||
|
||||
DrawableColorTester(Drawable drawable, boolean filterTransparent) {
|
||||
DrawableColorTester(Drawable drawable, @QuantizerFilterType int filterType) {
|
||||
// Some applications use LayerDrawable for their windowBackground. To ensure that we
|
||||
// only get the real background, so that the color is not affected by the alpha of the
|
||||
// upper layer, try to get the lower layer here. This can also speed up the calculation.
|
||||
@@ -570,12 +585,12 @@ public class SplashscreenContentDrawer {
|
||||
} else {
|
||||
mColorChecker = drawable instanceof ColorDrawable
|
||||
? new SingleColorTester((ColorDrawable) drawable)
|
||||
: new ComplexDrawableTester(drawable, filterTransparent);
|
||||
: new ComplexDrawableTester(drawable, filterType);
|
||||
}
|
||||
}
|
||||
|
||||
public float nonTransparentRatio() {
|
||||
return mColorChecker.nonTransparentRatio();
|
||||
public float passFilterRatio() {
|
||||
return mColorChecker.passFilterRatio();
|
||||
}
|
||||
|
||||
public boolean isComplexColor() {
|
||||
@@ -594,7 +609,7 @@ public class SplashscreenContentDrawer {
|
||||
* A help class to check the color information from a Drawable.
|
||||
*/
|
||||
private interface ColorTester {
|
||||
float nonTransparentRatio();
|
||||
float passFilterRatio();
|
||||
|
||||
boolean isComplexColor();
|
||||
|
||||
@@ -622,7 +637,7 @@ public class SplashscreenContentDrawer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float nonTransparentRatio() {
|
||||
public float passFilterRatio() {
|
||||
final int alpha = mColorDrawable.getAlpha();
|
||||
return (float) (alpha / 255);
|
||||
}
|
||||
@@ -651,15 +666,21 @@ public class SplashscreenContentDrawer {
|
||||
private static final int MAX_BITMAP_SIZE = 40;
|
||||
private final Palette mPalette;
|
||||
private final boolean mFilterTransparent;
|
||||
private static final TransparentFilterQuantizer TRANSPARENT_FILTER_QUANTIZER =
|
||||
new TransparentFilterQuantizer();
|
||||
private static final AlphaFilterQuantizer ALPHA_FILTER_QUANTIZER =
|
||||
new AlphaFilterQuantizer();
|
||||
|
||||
ComplexDrawableTester(Drawable drawable, boolean filterTransparent) {
|
||||
/**
|
||||
* @param drawable The test target.
|
||||
* @param filterType Targeting to filter out transparent or translucent pixels,
|
||||
* this would be needed if want to check
|
||||
* {@link #passFilterRatio()}, also affecting the estimated result
|
||||
* of the dominant color.
|
||||
*/
|
||||
ComplexDrawableTester(Drawable drawable, @QuantizerFilterType int filterType) {
|
||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ComplexDrawableTester");
|
||||
final Rect initialBounds = drawable.copyBounds();
|
||||
int width = drawable.getIntrinsicWidth();
|
||||
int height = drawable.getIntrinsicHeight();
|
||||
|
||||
// Some drawables do not have intrinsic dimensions
|
||||
if (width <= 0 || height <= 0) {
|
||||
width = MAX_BITMAP_SIZE;
|
||||
@@ -680,9 +701,10 @@ public class SplashscreenContentDrawer {
|
||||
// The Palette API will ignore Alpha, so it cannot handle transparent pixels, but
|
||||
// sometimes we will need this information to know if this Drawable object is
|
||||
// transparent.
|
||||
mFilterTransparent = filterTransparent;
|
||||
mFilterTransparent = filterType != NO_ALPHA_FILTER;
|
||||
if (mFilterTransparent) {
|
||||
builder = new Palette.Builder(bitmap, TRANSPARENT_FILTER_QUANTIZER)
|
||||
ALPHA_FILTER_QUANTIZER.setFilter(filterType);
|
||||
builder = new Palette.Builder(bitmap, ALPHA_FILTER_QUANTIZER)
|
||||
.maximumColorCount(5);
|
||||
} else {
|
||||
builder = new Palette.Builder(bitmap, null)
|
||||
@@ -694,8 +716,8 @@ public class SplashscreenContentDrawer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float nonTransparentRatio() {
|
||||
return mFilterTransparent ? TRANSPARENT_FILTER_QUANTIZER.mNonTransparentRatio : 1;
|
||||
public float passFilterRatio() {
|
||||
return mFilterTransparent ? ALPHA_FILTER_QUANTIZER.mPassFilterRatio : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -726,17 +748,34 @@ public class SplashscreenContentDrawer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class TransparentFilterQuantizer implements Quantizer {
|
||||
private static class AlphaFilterQuantizer implements Quantizer {
|
||||
private static final int NON_TRANSPARENT = 0xFF000000;
|
||||
private final Quantizer mInnerQuantizer = new VariationalKMeansQuantizer();
|
||||
private float mNonTransparentRatio;
|
||||
private final IntPredicate mTransparentFilter = i -> (i & NON_TRANSPARENT) != 0;
|
||||
private final IntPredicate mTranslucentFilter = i ->
|
||||
(i & NON_TRANSPARENT) == NON_TRANSPARENT;
|
||||
|
||||
private IntPredicate mFilter = mTransparentFilter;
|
||||
private float mPassFilterRatio;
|
||||
|
||||
void setFilter(@QuantizerFilterType int filterType) {
|
||||
switch (filterType) {
|
||||
case TRANSLUCENT_FILTER:
|
||||
mFilter = mTranslucentFilter;
|
||||
break;
|
||||
case TRANSPARENT_FILTER:
|
||||
default:
|
||||
mFilter = mTransparentFilter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void quantize(final int[] pixels, final int maxColors) {
|
||||
mNonTransparentRatio = 0;
|
||||
mPassFilterRatio = 0;
|
||||
int realSize = 0;
|
||||
for (int i = pixels.length - 1; i > 0; i--) {
|
||||
if ((pixels[i] & NON_TRANSPARENT) != 0) {
|
||||
if (mFilter.test(pixels[i])) {
|
||||
realSize++;
|
||||
}
|
||||
}
|
||||
@@ -747,11 +786,11 @@ public class SplashscreenContentDrawer {
|
||||
mInnerQuantizer.quantize(pixels, maxColors);
|
||||
return;
|
||||
}
|
||||
mNonTransparentRatio = (float) realSize / pixels.length;
|
||||
mPassFilterRatio = (float) realSize / pixels.length;
|
||||
final int[] samplePixels = new int[realSize];
|
||||
int rowIndex = 0;
|
||||
for (int i = pixels.length - 1; i > 0; i--) {
|
||||
if ((pixels[i] & NON_TRANSPARENT) == NON_TRANSPARENT) {
|
||||
if (mFilter.test(pixels[i])) {
|
||||
samplePixels[rowIndex] = pixels[i];
|
||||
rowIndex++;
|
||||
}
|
||||
@@ -810,16 +849,16 @@ public class SplashscreenContentDrawer {
|
||||
final int mBgColor;
|
||||
final boolean mIsBgComplex;
|
||||
final boolean mIsBgGrayscale;
|
||||
final float mFgNonTransparentRatio;
|
||||
final float mFgNonTranslucentRatio;
|
||||
|
||||
IconColor(int hash, int fgColor, int bgColor, boolean isBgComplex,
|
||||
boolean isBgGrayscale, float fgNonTransparentRatio) {
|
||||
boolean isBgGrayscale, float fgNonTranslucnetRatio) {
|
||||
super(hash);
|
||||
mFgColor = fgColor;
|
||||
mBgColor = bgColor;
|
||||
mIsBgComplex = isBgComplex;
|
||||
mIsBgGrayscale = isBgGrayscale;
|
||||
mFgNonTransparentRatio = fgNonTransparentRatio;
|
||||
mFgNonTranslucentRatio = fgNonTranslucnetRatio;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -905,7 +944,7 @@ public class SplashscreenContentDrawer {
|
||||
final DrawableColorTester bgTester = bgColorTesterSupplier.get();
|
||||
final IconColor iconColor = new IconColor(hash, fgTester.getDominateColor(),
|
||||
bgTester.getDominateColor(), bgTester.isComplexColor(), bgTester.isGrayscale(),
|
||||
fgTester.nonTransparentRatio());
|
||||
fgTester.passFilterRatio());
|
||||
colors.mIconColors[leastUsedIndex[0]] = iconColor;
|
||||
return iconColor;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user