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