Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2ff050f authored by Wei Sheng Shih's avatar Wei Sheng Shih Committed by Android (Google) Code Review
Browse files

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

parents 0793f65c 331615ad
Loading
Loading
Loading
Loading
+71 −32
Original line number Diff line number Diff line
@@ -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;
        }