Loading core/java/android/app/WallpaperColors.java +60 −27 Original line number Diff line number Diff line Loading @@ -30,14 +30,17 @@ import android.util.Log; import android.util.Size; 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.VariationalKMeansQuantizer; import com.android.internal.util.ContrastColorUtil; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * Provides information about the colors of a wallpaper. Loading Loading @@ -94,16 +97,21 @@ public final class WallpaperColors implements Parcelable { private static final float DARK_PIXEL_CONTRAST = 6f; private static final float MAX_DARK_AREA = 0.025f; private final ArrayList<Color> mMainColors; private final List<Color> mMainColors; private final Map<Integer, Integer> mAllColors; private int mColorHints; public WallpaperColors(Parcel parcel) { mMainColors = new ArrayList<>(); mAllColors = new HashMap<>(); final int count = parcel.readInt(); for (int i = 0; i < count; i++) { final int colorInt = parcel.readInt(); Color color = Color.valueOf(colorInt); mMainColors.add(color); final int population = parcel.readInt(); mAllColors.put(colorInt, population); } mColorHints = parcel.readInt(); } Loading Loading @@ -166,39 +174,22 @@ public final class WallpaperColors implements Parcelable { } final Palette palette = Palette .from(bitmap) .setQuantizer(new VariationalKMeansQuantizer()) .maximumColorCount(5) .clearFilters() .from(bitmap, new CelebiQuantizer()) .maximumColorCount(256) .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA) .generate(); // Remove insignificant colors and sort swatches by population 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()); final int swatchesSize = swatches.size(); Color primary = null, secondary = null, tertiary = null; swatchLoop: final Map<Integer, Integer> populationByColor = new HashMap<>(); for (int i = 0; i < swatchesSize; i++) { Color color = Color.valueOf(swatches.get(i).getRgb()); switch (i) { case 0: primary = color; break; case 1: secondary = color; break; case 2: tertiary = color; break; default: // out of bounds break swatchLoop; } Palette.Swatch swatch = swatches.get(i); int colorInt = swatch.getInt(); populationByColor.put(colorInt, swatch.getPopulation()); } int hints = calculateDarkHints(bitmap); Loading @@ -207,7 +198,7 @@ public final class WallpaperColors implements Parcelable { bitmap.recycle(); } return new WallpaperColors(primary, secondary, tertiary, HINT_FROM_BITMAP | hints); return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints); } /** Loading Loading @@ -253,9 +244,13 @@ public final class WallpaperColors implements Parcelable { } mMainColors = new ArrayList<>(3); mAllColors = new HashMap<>(); mMainColors.add(primaryColor); mAllColors.put(primaryColor.toArgb(), 0); if (secondaryColor != null) { mMainColors.add(secondaryColor); mAllColors.put(secondaryColor.toArgb(), 0); } if (tertiaryColor != null) { if (secondaryColor == null) { Loading @@ -263,8 +258,32 @@ public final class WallpaperColors implements Parcelable { + "secondaryColor is null"); } 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; } Loading Loading @@ -293,6 +312,9 @@ public final class WallpaperColors implements Parcelable { for (int i = 0; i < count; i++) { Color color = mainColors.get(i); dest.writeInt(color.toArgb()); Integer population = mAllColors.get(color.toArgb()); int populationInt = (population != null) ? population : 0; dest.writeInt(populationInt); } dest.writeInt(mColorHints); } Loading Loading @@ -336,6 +358,17 @@ public final class WallpaperColors implements Parcelable { 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 public boolean equals(@Nullable Object o) { if (o == null || getClass() != o.getClass()) { Loading core/java/com/android/internal/graphics/palette/CelebiQuantizer.java 0 → 100644 +50 −0 Original line number 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; } } core/java/com/android/internal/graphics/palette/CentroidProvider.java 0 → 100644 +38 −0 Original line number 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); } core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java +10 −42 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ package com.android.internal.graphics.palette; import android.graphics.Color; import android.util.TimingLogger; import com.android.internal.graphics.palette.Palette.Swatch; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; Loading @@ -42,9 +44,6 @@ import java.util.Comparator; import java.util.List; 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/ * graphics/ColorCutQuantizer.java Loading Loading @@ -77,7 +76,6 @@ final class ColorCutQuantizer implements Quantizer { int[] mHistogram; List<Swatch> mQuantizedColors; TimingLogger mTimingLogger; Palette.Filter[] mFilters; private final float[] mTempHsl = new float[3]; Loading @@ -86,11 +84,9 @@ final class ColorCutQuantizer implements Quantizer { * * @param pixels histogram representing an image's pixel data * @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, final Palette.Filter[] filters) { public void quantize(final int[] pixels, final int maxColors) { mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null; mFilters = filters; final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)]; for (int i = 0; i < pixels.length; i++) { Loading @@ -108,10 +104,6 @@ final class ColorCutQuantizer implements Quantizer { // Now let's count the number of distinct colors int distinctColorCount = 0; 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 the color has population, increase the distinct color count distinctColorCount++; Loading Loading @@ -216,12 +208,8 @@ final class ColorCutQuantizer implements Quantizer { ArrayList<Swatch> colors = new ArrayList<>(vboxes.size()); for (Vbox vbox : vboxes) { 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); } } return colors; } Loading @@ -230,7 +218,7 @@ final class ColorCutQuantizer implements Quantizer { */ private class Vbox { // lower and upper index are inclusive private int mLowerIndex; private final int mLowerIndex; private int mUpperIndex; // Population of colors within this box private int mPopulation; Loading Loading @@ -447,27 +435,6 @@ 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 */ Loading Loading @@ -498,7 +465,8 @@ final class ColorCutQuantizer implements Quantizer { } private static int approximateToRgb888(int color) { return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color)); return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color)); } /** Loading core/java/com/android/internal/graphics/palette/Contrast.java 0 → 100644 +103 −0 Original line number 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
core/java/android/app/WallpaperColors.java +60 −27 Original line number Diff line number Diff line Loading @@ -30,14 +30,17 @@ import android.util.Log; import android.util.Size; 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.VariationalKMeansQuantizer; import com.android.internal.util.ContrastColorUtil; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * Provides information about the colors of a wallpaper. Loading Loading @@ -94,16 +97,21 @@ public final class WallpaperColors implements Parcelable { private static final float DARK_PIXEL_CONTRAST = 6f; private static final float MAX_DARK_AREA = 0.025f; private final ArrayList<Color> mMainColors; private final List<Color> mMainColors; private final Map<Integer, Integer> mAllColors; private int mColorHints; public WallpaperColors(Parcel parcel) { mMainColors = new ArrayList<>(); mAllColors = new HashMap<>(); final int count = parcel.readInt(); for (int i = 0; i < count; i++) { final int colorInt = parcel.readInt(); Color color = Color.valueOf(colorInt); mMainColors.add(color); final int population = parcel.readInt(); mAllColors.put(colorInt, population); } mColorHints = parcel.readInt(); } Loading Loading @@ -166,39 +174,22 @@ public final class WallpaperColors implements Parcelable { } final Palette palette = Palette .from(bitmap) .setQuantizer(new VariationalKMeansQuantizer()) .maximumColorCount(5) .clearFilters() .from(bitmap, new CelebiQuantizer()) .maximumColorCount(256) .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA) .generate(); // Remove insignificant colors and sort swatches by population 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()); final int swatchesSize = swatches.size(); Color primary = null, secondary = null, tertiary = null; swatchLoop: final Map<Integer, Integer> populationByColor = new HashMap<>(); for (int i = 0; i < swatchesSize; i++) { Color color = Color.valueOf(swatches.get(i).getRgb()); switch (i) { case 0: primary = color; break; case 1: secondary = color; break; case 2: tertiary = color; break; default: // out of bounds break swatchLoop; } Palette.Swatch swatch = swatches.get(i); int colorInt = swatch.getInt(); populationByColor.put(colorInt, swatch.getPopulation()); } int hints = calculateDarkHints(bitmap); Loading @@ -207,7 +198,7 @@ public final class WallpaperColors implements Parcelable { bitmap.recycle(); } return new WallpaperColors(primary, secondary, tertiary, HINT_FROM_BITMAP | hints); return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints); } /** Loading Loading @@ -253,9 +244,13 @@ public final class WallpaperColors implements Parcelable { } mMainColors = new ArrayList<>(3); mAllColors = new HashMap<>(); mMainColors.add(primaryColor); mAllColors.put(primaryColor.toArgb(), 0); if (secondaryColor != null) { mMainColors.add(secondaryColor); mAllColors.put(secondaryColor.toArgb(), 0); } if (tertiaryColor != null) { if (secondaryColor == null) { Loading @@ -263,8 +258,32 @@ public final class WallpaperColors implements Parcelable { + "secondaryColor is null"); } 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; } Loading Loading @@ -293,6 +312,9 @@ public final class WallpaperColors implements Parcelable { for (int i = 0; i < count; i++) { Color color = mainColors.get(i); dest.writeInt(color.toArgb()); Integer population = mAllColors.get(color.toArgb()); int populationInt = (population != null) ? population : 0; dest.writeInt(populationInt); } dest.writeInt(mColorHints); } Loading Loading @@ -336,6 +358,17 @@ public final class WallpaperColors implements Parcelable { 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 public boolean equals(@Nullable Object o) { if (o == null || getClass() != o.getClass()) { Loading
core/java/com/android/internal/graphics/palette/CelebiQuantizer.java 0 → 100644 +50 −0 Original line number 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; } }
core/java/com/android/internal/graphics/palette/CentroidProvider.java 0 → 100644 +38 −0 Original line number 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); }
core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java +10 −42 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ package com.android.internal.graphics.palette; import android.graphics.Color; import android.util.TimingLogger; import com.android.internal.graphics.palette.Palette.Swatch; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; Loading @@ -42,9 +44,6 @@ import java.util.Comparator; import java.util.List; 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/ * graphics/ColorCutQuantizer.java Loading Loading @@ -77,7 +76,6 @@ final class ColorCutQuantizer implements Quantizer { int[] mHistogram; List<Swatch> mQuantizedColors; TimingLogger mTimingLogger; Palette.Filter[] mFilters; private final float[] mTempHsl = new float[3]; Loading @@ -86,11 +84,9 @@ final class ColorCutQuantizer implements Quantizer { * * @param pixels histogram representing an image's pixel data * @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, final Palette.Filter[] filters) { public void quantize(final int[] pixels, final int maxColors) { mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null; mFilters = filters; final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)]; for (int i = 0; i < pixels.length; i++) { Loading @@ -108,10 +104,6 @@ final class ColorCutQuantizer implements Quantizer { // Now let's count the number of distinct colors int distinctColorCount = 0; 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 the color has population, increase the distinct color count distinctColorCount++; Loading Loading @@ -216,12 +208,8 @@ final class ColorCutQuantizer implements Quantizer { ArrayList<Swatch> colors = new ArrayList<>(vboxes.size()); for (Vbox vbox : vboxes) { 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); } } return colors; } Loading @@ -230,7 +218,7 @@ final class ColorCutQuantizer implements Quantizer { */ private class Vbox { // lower and upper index are inclusive private int mLowerIndex; private final int mLowerIndex; private int mUpperIndex; // Population of colors within this box private int mPopulation; Loading Loading @@ -447,27 +435,6 @@ 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 */ Loading Loading @@ -498,7 +465,8 @@ final class ColorCutQuantizer implements Quantizer { } private static int approximateToRgb888(int color) { return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color)); return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color)); } /** Loading
core/java/com/android/internal/graphics/palette/Contrast.java 0 → 100644 +103 −0 Original line number 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); } }