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

Commit e36c4de1 authored by Lucas Dupin's avatar Lucas Dupin Committed by Android (Google) Code Review
Browse files

Merge "Revert "[framework] Integrate new quantizers"" into sc-dev

parents 2b821d14 96169052
Loading
Loading
Loading
Loading
+27 −60
Original line number Original line Diff line number Diff line
@@ -30,17 +30,14 @@ import android.util.Log;
import android.util.Size;
import android.util.Size;


import com.android.internal.graphics.ColorUtils;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.graphics.palette.CelebiQuantizer;
import com.android.internal.graphics.palette.Palette;
import com.android.internal.graphics.palette.Palette;
import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.util.ContrastColorUtil;


import java.io.FileOutputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
/**
 * Provides information about the colors of a wallpaper.
 * Provides information about the colors of a wallpaper.
@@ -97,21 +94,16 @@ public final class WallpaperColors implements Parcelable {
    private static final float DARK_PIXEL_CONTRAST = 6f;
    private static final float DARK_PIXEL_CONTRAST = 6f;
    private static final float MAX_DARK_AREA = 0.025f;
    private static final float MAX_DARK_AREA = 0.025f;


    private final List<Color> mMainColors;
    private final ArrayList<Color> mMainColors;
    private final Map<Integer, Integer> mAllColors;
    private int mColorHints;
    private int mColorHints;


    public WallpaperColors(Parcel parcel) {
    public WallpaperColors(Parcel parcel) {
        mMainColors = new ArrayList<>();
        mMainColors = new ArrayList<>();
        mAllColors = new HashMap<>();
        final int count = parcel.readInt();
        final int count = parcel.readInt();
        for (int i = 0; i < count; i++) {
        for (int i = 0; i < count; i++) {
            final int colorInt = parcel.readInt();
            final int colorInt = parcel.readInt();
            Color color = Color.valueOf(colorInt);
            Color color = Color.valueOf(colorInt);
            mMainColors.add(color);
            mMainColors.add(color);

            final int population = parcel.readInt();
            mAllColors.put(colorInt, population);
        }
        }
        mColorHints = parcel.readInt();
        mColorHints = parcel.readInt();
    }
    }
@@ -174,22 +166,39 @@ public final class WallpaperColors implements Parcelable {
        }
        }


        final Palette palette = Palette
        final Palette palette = Palette
                .from(bitmap, new CelebiQuantizer())
                .from(bitmap)
                .maximumColorCount(256)
                .setQuantizer(new VariationalKMeansQuantizer())
                .maximumColorCount(5)
                .clearFilters()
                .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
                .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
                .generate();
                .generate();

        // Remove insignificant colors and sort swatches by population
        // Remove insignificant colors and sort swatches by population
        final ArrayList<Palette.Swatch> swatches = new ArrayList<>(palette.getSwatches());
        final ArrayList<Palette.Swatch> swatches = new ArrayList<>(palette.getSwatches());
        final float minColorArea = bitmap.getWidth() * bitmap.getHeight() * MIN_COLOR_OCCURRENCE;
        swatches.removeIf(s -> s.getPopulation() < minColorArea);
        swatches.sort((a, b) -> b.getPopulation() - a.getPopulation());
        swatches.sort((a, b) -> b.getPopulation() - a.getPopulation());


        final int swatchesSize = swatches.size();
        final int swatchesSize = swatches.size();
        Color primary = null, secondary = null, tertiary = null;


        final Map<Integer, Integer> populationByColor = new HashMap<>();
        swatchLoop:
        for (int i = 0; i < swatchesSize; i++) {
        for (int i = 0; i < swatchesSize; i++) {
            Palette.Swatch swatch = swatches.get(i);
            Color color = Color.valueOf(swatches.get(i).getRgb());
            int colorInt = swatch.getInt();
            switch (i) {
            populationByColor.put(colorInt, swatch.getPopulation());
                case 0:

                    primary = color;
                    break;
                case 1:
                    secondary = color;
                    break;
                case 2:
                    tertiary = color;
                    break;
                default:
                    // out of bounds
                    break swatchLoop;
            }
        }
        }


        int hints = calculateDarkHints(bitmap);
        int hints = calculateDarkHints(bitmap);
@@ -198,7 +207,7 @@ public final class WallpaperColors implements Parcelable {
            bitmap.recycle();
            bitmap.recycle();
        }
        }


        return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints);
        return new WallpaperColors(primary, secondary, tertiary, HINT_FROM_BITMAP | hints);
    }
    }


    /**
    /**
@@ -244,13 +253,9 @@ public final class WallpaperColors implements Parcelable {
        }
        }


        mMainColors = new ArrayList<>(3);
        mMainColors = new ArrayList<>(3);
        mAllColors = new HashMap<>();

        mMainColors.add(primaryColor);
        mMainColors.add(primaryColor);
        mAllColors.put(primaryColor.toArgb(), 0);
        if (secondaryColor != null) {
        if (secondaryColor != null) {
            mMainColors.add(secondaryColor);
            mMainColors.add(secondaryColor);
            mAllColors.put(secondaryColor.toArgb(), 0);
        }
        }
        if (tertiaryColor != null) {
        if (tertiaryColor != null) {
            if (secondaryColor == null) {
            if (secondaryColor == null) {
@@ -258,32 +263,8 @@ public final class WallpaperColors implements Parcelable {
                        + "secondaryColor is null");
                        + "secondaryColor is null");
            }
            }
            mMainColors.add(tertiaryColor);
            mMainColors.add(tertiaryColor);
            mAllColors.put(tertiaryColor.toArgb(), 0);
        }
        mColorHints = colorHints;
        }
        }


    /**
     * Constructs a new object from a set of colors, where hints can be specified.
     *
     * @param populationByColor Map with keys of colors, and value representing the number of
     *                          occurrences of color in the wallpaper.
     * @param colorHints        A combination of WallpaperColor hints.
     * @hide
     * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
     * @see WallpaperColors#fromBitmap(Bitmap)
     * @see WallpaperColors#fromDrawable(Drawable)
     */
    public WallpaperColors(@NonNull Map<Integer, Integer> populationByColor, int colorHints) {
        mAllColors = populationByColor;

        ArrayList<Map.Entry<Integer, Integer>> mapEntries = new ArrayList(
                populationByColor.entrySet());
        mapEntries.sort((a, b) ->
                a.getValue().compareTo(b.getValue())
        );
        mMainColors = mapEntries.stream().map(entry -> Color.valueOf(entry.getKey())).collect(
                Collectors.toList());
        mColorHints = colorHints;
        mColorHints = colorHints;
    }
    }


@@ -312,9 +293,6 @@ public final class WallpaperColors implements Parcelable {
        for (int i = 0; i < count; i++) {
        for (int i = 0; i < count; i++) {
            Color color = mainColors.get(i);
            Color color = mainColors.get(i);
            dest.writeInt(color.toArgb());
            dest.writeInt(color.toArgb());
            Integer population = mAllColors.get(color.toArgb());
            int populationInt = (population != null) ? population : 0;
            dest.writeInt(populationInt);
        }
        }
        dest.writeInt(mColorHints);
        dest.writeInt(mColorHints);
    }
    }
@@ -358,17 +336,6 @@ public final class WallpaperColors implements Parcelable {
        return Collections.unmodifiableList(mMainColors);
        return Collections.unmodifiableList(mMainColors);
    }
    }


    /**
     * Map of all colors. Key is rgb integer, value is importance of color.
     *
     * @return List of colors.
     * @hide
     */
    public @NonNull Map<Integer, Integer> getAllColors() {
        return Collections.unmodifiableMap(mAllColors);
    }


    @Override
    @Override
    public boolean equals(@Nullable Object o) {
    public boolean equals(@Nullable Object o) {
        if (o == null || getClass() != o.getClass()) {
        if (o == null || getClass() != o.getClass()) {
+0 −50
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.graphics.palette;

import java.util.List;

/**
 * An implementation of Celebi's WSM quantizer, or, a Kmeans quantizer that starts with centroids
 * from a Wu quantizer to ensure 100% reproducible and quality results, and has some optimizations
 * to the Kmeans algorithm.
 *
 * See Celebi 2011, “Improving the Performance of K-Means for Color Quantization”
 */
public class CelebiQuantizer implements Quantizer {
    private List<Palette.Swatch> mSwatches;

    public CelebiQuantizer() { }

    @Override
    public void quantize(int[] pixels, int maxColors) {
        WuQuantizer wu = new WuQuantizer(pixels, maxColors);
        wu.quantize(pixels, maxColors);
        List<Palette.Swatch> wuSwatches = wu.getQuantizedColors();
        LABCentroid labCentroidProvider = new LABCentroid();
        WSMeansQuantizer kmeans =
                new WSMeansQuantizer(WSMeansQuantizer.createStartingCentroids(labCentroidProvider,
                        wuSwatches), labCentroidProvider, pixels, maxColors);
        kmeans.quantize(pixels, maxColors);
        mSwatches = kmeans.getQuantizedColors();
    }

    @Override
    public List<Palette.Swatch> getQuantizedColors() {
        return mSwatches;
    }
}
+0 −38
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.graphics.palette;

import android.annotation.ColorInt;

interface CentroidProvider {
    /**
     * @return 3 dimensions representing the color
     */
    float[] getCentroid(@ColorInt int color);

    /**
     * @param centroid 3 dimensions representing the color
     * @return 32-bit ARGB representation
     */
    @ColorInt
    int getColor(float[] centroid);

    /**
     * Distance between two centroids.
     */
    float distance(float[] a, float[] b);
}
+42 −10
Original line number Original line Diff line number Diff line
@@ -35,8 +35,6 @@ package com.android.internal.graphics.palette;
import android.graphics.Color;
import android.graphics.Color;
import android.util.TimingLogger;
import android.util.TimingLogger;


import com.android.internal.graphics.palette.Palette.Swatch;

import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collection;
@@ -44,6 +42,9 @@ import java.util.Comparator;
import java.util.List;
import java.util.List;
import java.util.PriorityQueue;
import java.util.PriorityQueue;


import com.android.internal.graphics.ColorUtils;
import com.android.internal.graphics.palette.Palette.Swatch;

/**
/**
 * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/
 * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/
 * graphics/ColorCutQuantizer.java
 * graphics/ColorCutQuantizer.java
@@ -76,6 +77,7 @@ final class ColorCutQuantizer implements Quantizer {
    int[] mHistogram;
    int[] mHistogram;
    List<Swatch> mQuantizedColors;
    List<Swatch> mQuantizedColors;
    TimingLogger mTimingLogger;
    TimingLogger mTimingLogger;
    Palette.Filter[] mFilters;


    private final float[] mTempHsl = new float[3];
    private final float[] mTempHsl = new float[3];


@@ -84,9 +86,11 @@ final class ColorCutQuantizer implements Quantizer {
     *
     *
     * @param pixels histogram representing an image's pixel data
     * @param pixels histogram representing an image's pixel data
     * @param maxColors The maximum number of colors that should be in the result palette.
     * @param maxColors The maximum number of colors that should be in the result palette.
     * @param filters Set of filters to use in the quantization stage
     */
     */
    public void quantize(final int[] pixels, final int maxColors) {
    public void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters) {
        mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null;
        mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null;
        mFilters = filters;


        final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)];
        final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)];
        for (int i = 0; i < pixels.length; i++) {
        for (int i = 0; i < pixels.length; i++) {
@@ -104,6 +108,10 @@ final class ColorCutQuantizer implements Quantizer {
        // Now let's count the number of distinct colors
        // Now let's count the number of distinct colors
        int distinctColorCount = 0;
        int distinctColorCount = 0;
        for (int color = 0; color < hist.length; color++) {
        for (int color = 0; color < hist.length; color++) {
            if (hist[color] > 0 && shouldIgnoreColor(color)) {
                // If we should ignore the color, set the population to 0
                hist[color] = 0;
            }
            if (hist[color] > 0) {
            if (hist[color] > 0) {
                // If the color has population, increase the distinct color count
                // If the color has population, increase the distinct color count
                distinctColorCount++;
                distinctColorCount++;
@@ -208,8 +216,12 @@ final class ColorCutQuantizer implements Quantizer {
        ArrayList<Swatch> colors = new ArrayList<>(vboxes.size());
        ArrayList<Swatch> colors = new ArrayList<>(vboxes.size());
        for (Vbox vbox : vboxes) {
        for (Vbox vbox : vboxes) {
            Swatch swatch = vbox.getAverageColor();
            Swatch swatch = vbox.getAverageColor();
            if (!shouldIgnoreColor(swatch)) {
                // As we're averaging a color box, we can still get colors which we do not want, so
                // we check again here
                colors.add(swatch);
                colors.add(swatch);
            }
            }
        }
        return colors;
        return colors;
    }
    }


@@ -218,7 +230,7 @@ final class ColorCutQuantizer implements Quantizer {
     */
     */
    private class Vbox {
    private class Vbox {
        // lower and upper index are inclusive
        // lower and upper index are inclusive
        private final int mLowerIndex;
        private int mLowerIndex;
        private int mUpperIndex;
        private int mUpperIndex;
        // Population of colors within this box
        // Population of colors within this box
        private int mPopulation;
        private int mPopulation;
@@ -435,6 +447,27 @@ final class ColorCutQuantizer implements Quantizer {
        }
        }
    }
    }


    private boolean shouldIgnoreColor(int color565) {
        final int rgb = approximateToRgb888(color565);
        ColorUtils.colorToHSL(rgb, mTempHsl);
        return shouldIgnoreColor(rgb, mTempHsl);
    }

    private boolean shouldIgnoreColor(Swatch color) {
        return shouldIgnoreColor(color.getRgb(), color.getHsl());
    }

    private boolean shouldIgnoreColor(int rgb, float[] hsl) {
        if (mFilters != null && mFilters.length > 0) {
            for (int i = 0, count = mFilters.length; i < count; i++) {
                if (!mFilters[i].isAllowed(rgb, hsl)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
    /**
     * Comparator which sorts {@link Vbox} instances based on their volume, in descending order
     * Comparator which sorts {@link Vbox} instances based on their volume, in descending order
     */
     */
@@ -465,8 +498,7 @@ final class ColorCutQuantizer implements Quantizer {
    }
    }


    private static int approximateToRgb888(int color) {
    private static int approximateToRgb888(int color) {
        return approximateToRgb888(quantizedRed(color), quantizedGreen(color),
        return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color));
                quantizedBlue(color));
    }
    }


    /**
    /**
+0 −103
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.graphics.palette;

/**
 * Helper methods for determining contrast between two colors, either via the colors themselves
 * or components in different color spaces.
 */
public class Contrast {
    /**
     *
     * @param y Y in XYZ that contrasts with the returned Y value
     * @param contrast contrast ratio between color argument and returned Y value. Must be >= 1
     *    or an exception will be thrown
     * @return the lower Y coordinate in XYZ space that contrasts with color, or -1 if reaching
     *    no Y coordinate reaches contrast with color.
     */
    public static float lighterY(float y, float contrast) {
        assert (contrast >= 1);
        float answer = -5 + contrast * (5 + y);
        if (answer > 100.0) {
            return -1;
        }
        return answer;
    }


    /**
     * @param y Y in XYZ that contrasts with the returned Y value
     * @param contrast contrast ratio between color argument and returned Y value. Must be >= 1
     *    or an exception will be thrown
     * @return the lower Y coordinate in XYZ space that contrasts with color, or -1 if reaching
     *    no Y coordinate reaches contrast with color.
     */
    public static float darkerY(float y, float contrast) {
        assert (contrast >= 1);
        float answer = (5 - 5 * contrast + y) / contrast;
        if (answer < 0.0) {
            return -1;
        }
        return answer;
    }

    /**
     * Convert L* in L*a*b* to Y in XYZ.
     *
     * @param lstar L* in L*a*b*
     * @return Y in XYZ
     */
    public static float lstarToY(float lstar) {
        // http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
        float ke = 8.0f;
        if (lstar > ke) {
            return (float) (Math.pow(((lstar + 16.0) / 116.0), 3) * 100.0);
        } else {
            return (float) (lstar / (24389 / 27) * 100.0);
        }
    }

    /**
     * Convert Y in XYZ to L* in L*a*b*.
     *
     * @param y Y in XYZ
     * @return L* in L*a*b*
     */
    public static float yToLstar(float y) {
        y = y / 100.0f;
        float e = 216.0f / 24389.0f;
        float y_intermediate;
        if (y <= e) {
            y_intermediate = (24389.f / 27.f) * y;
            // If y < e, can skip consecutive steps of / 116 + 16 followed by * 116 - 16.
            return y_intermediate;
        } else {
            y_intermediate = (float) Math.cbrt(y);
        }
        return 116.f * y_intermediate - 16.f;
    }


    /**
     * @return Contrast ratio between two Y values in XYZ space.
     */
    public static float contrastYs(float y1, float y2) {
        final float lighter = Math.max(y1, y2);
        final float darker = (lighter == y1) ? y2 : y1;
        return (lighter + 5) / (darker + 5);
    }
}
Loading