Merge "Fine tune splash screen icon generation algorithm" into sc-dev

This commit is contained in:
Wei Sheng Shih
2021-07-23 13:31:27 +00:00
committed by Android (Google) Code Review

View File

@@ -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;
}