FileDocCategorySizeDatePackage
Palette.javaAPI DocAndroid 5.1 API24932Thu Mar 12 22:22:56 GMT 2015android.support.v7.graphics

Palette

public final class Palette extends Object
A helper class to extract prominent colors from an image.

A number of colors with different profiles are extracted from the image:

  • Vibrant
  • Vibrant Dark
  • Vibrant Light
  • Muted
  • Muted Dark
  • Muted Light
These can be retrieved from the appropriate getter method.

Instances can be created with the synchronous factory methods {@link #generate(Bitmap)} and {@link #generate(Bitmap, int)}.

These should be called on a background thread, ideally the one in which you load your images on. Sometimes that is not possible, so asynchronous factory methods have also been provided: {@link #generateAsync(Bitmap, PaletteAsyncListener)} and {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}. These can be used as so:

Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
public void onGenerated(Palette palette) {
// Do something with colors...
}
});

Fields Summary
private static final int
CALCULATE_BITMAP_MIN_DIMENSION
private static final int
DEFAULT_CALCULATE_NUMBER_COLORS
private static final float
TARGET_DARK_LUMA
private static final float
MAX_DARK_LUMA
private static final float
MIN_LIGHT_LUMA
private static final float
TARGET_LIGHT_LUMA
private static final float
MIN_NORMAL_LUMA
private static final float
TARGET_NORMAL_LUMA
private static final float
MAX_NORMAL_LUMA
private static final float
TARGET_MUTED_SATURATION
private static final float
MAX_MUTED_SATURATION
private static final float
TARGET_VIBRANT_SATURATION
private static final float
MIN_VIBRANT_SATURATION
private static final float
WEIGHT_SATURATION
private static final float
WEIGHT_LUMA
private static final float
WEIGHT_POPULATION
private static final float
MIN_CONTRAST_TITLE_TEXT
private static final float
MIN_CONTRAST_BODY_TEXT
private final List
mSwatches
private final int
mHighestPopulation
private Swatch
mVibrantSwatch
private Swatch
mMutedSwatch
private Swatch
mDarkVibrantSwatch
private Swatch
mDarkMutedSwatch
private Swatch
mLightVibrantSwatch
private Swatch
mLightMutedColor
Constructors Summary
private Palette(List swatches)

        mSwatches = swatches;
        mHighestPopulation = findMaxPopulation();

        mVibrantSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);

        mLightVibrantSwatch = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);

        mDarkVibrantSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);

        mMutedSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);

        mLightMutedColor = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);

        mDarkMutedSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);

        // Now try and generate any missing colors
        generateEmptySwatches();
    
Methods Summary
private static voidcheckAsyncListenerParam(android.support.v7.graphics.Palette$PaletteAsyncListener listener)

        if (listener == null) {
            throw new IllegalArgumentException("listener can not be null");
        }
    
private static voidcheckBitmapParam(android.graphics.Bitmap bitmap)

        if (bitmap == null) {
            throw new IllegalArgumentException("bitmap can not be null");
        }
        if (bitmap.isRecycled()) {
            throw new IllegalArgumentException("bitmap can not be recycled");
        }
    
private static voidcheckNumberColorsParam(int numColors)

        if (numColors < 1) {
            throw new IllegalArgumentException("numColors must be 1 of greater");
        }
    
private static float[]copyHslValues(android.support.v7.graphics.Palette$Swatch color)
Copy a {@link Swatch}'s HSL values into a new float[].

        final float[] newHsl = new float[3];
        System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);
        return newHsl;
    
private static floatcreateComparisonValue(float saturation, float targetSaturation, float luma, float targetLuma, int population, int highestPopulation)

        return weightedMean(
                invertDiff(saturation, targetSaturation), WEIGHT_SATURATION,
                invertDiff(luma, targetLuma), WEIGHT_LUMA,
                population / (float) highestPopulation, WEIGHT_POPULATION
        );
    
public booleanequals(java.lang.Object o)

        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Palette palette = (Palette) o;

        if (mSwatches != null ? !mSwatches.equals(palette.mSwatches) : palette.mSwatches != null) {
            return false;
        }
        if (mDarkMutedSwatch != null ? !mDarkMutedSwatch.equals(palette.mDarkMutedSwatch)
                : palette.mDarkMutedSwatch != null) {
            return false;
        }
        if (mDarkVibrantSwatch != null ? !mDarkVibrantSwatch.equals(palette.mDarkVibrantSwatch)
                : palette.mDarkVibrantSwatch != null) {
            return false;
        }
        if (mLightMutedColor != null ? !mLightMutedColor.equals(palette.mLightMutedColor)
                : palette.mLightMutedColor != null) {
            return false;
        }
        if (mLightVibrantSwatch != null ? !mLightVibrantSwatch.equals(palette.mLightVibrantSwatch)
                : palette.mLightVibrantSwatch != null) {
            return false;
        }
        if (mMutedSwatch != null ? !mMutedSwatch.equals(palette.mMutedSwatch)
                : palette.mMutedSwatch != null) {
            return false;
        }
        if (mVibrantSwatch != null ? !mVibrantSwatch.equals(palette.mVibrantSwatch)
                : palette.mVibrantSwatch != null) {
            return false;
        }

        return true;
    
private android.support.v7.graphics.Palette$SwatchfindColor(float targetLuma, float minLuma, float maxLuma, float targetSaturation, float minSaturation, float maxSaturation)

        Swatch max = null;
        float maxValue = 0f;

        for (Swatch swatch : mSwatches) {
            final float sat = swatch.getHsl()[1];
            final float luma = swatch.getHsl()[2];

            if (sat >= minSaturation && sat <= maxSaturation &&
                    luma >= minLuma && luma <= maxLuma &&
                    !isAlreadySelected(swatch)) {
                float thisValue = createComparisonValue(sat, targetSaturation, luma, targetLuma,
                        swatch.getPopulation(), mHighestPopulation);
                if (max == null || thisValue > maxValue) {
                    max = swatch;
                    maxValue = thisValue;
                }
            }
        }

        return max;
    
private intfindMaxPopulation()
Find the {@link Swatch} with the highest population value and return the population.

        int population = 0;
        for (Swatch swatch : mSwatches) {
            population = Math.max(population, swatch.getPopulation());
        }
        return population;
    
public static android.support.v7.graphics.Palettefrom(java.util.List swatches)
Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches. This is useful for testing, or if you want to resurrect a {@link Palette} instance from a list of swatches. Will return null if the {@code swatches} is null.

        if (swatches == null) {
            return null;
        }
        return new Palette(swatches);
    
public static android.support.v7.graphics.Palettegenerate(android.graphics.Bitmap bitmap)
Generate a {@link Palette} from a {@link Bitmap} using the default number of colors.


                       
         
        return generate(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS);
    
public static android.support.v7.graphics.Palettegenerate(android.graphics.Bitmap bitmap, int numColors)
Generate a {@link Palette} from a {@link Bitmap} using the specified {@code numColors}. Good values for {@code numColors} depend on the source image type. For landscapes, a good values are in the range 12-16. For images which are largely made up of people's faces then this value should be increased to 24-32.

param
numColors The maximum number of colors in the generated palette. Increasing this number will increase the time needed to compute the values.

        checkBitmapParam(bitmap);
        checkNumberColorsParam(numColors);

        // First we'll scale down the bitmap so it's shortest dimension is 100px
        final Bitmap scaledBitmap = scaleBitmapDown(bitmap);

        // Now generate a quantizer from the Bitmap
        ColorCutQuantizer quantizer = ColorCutQuantizer.fromBitmap(scaledBitmap, numColors);

        // If created a new bitmap, recycle it
        if (scaledBitmap != bitmap) {
            scaledBitmap.recycle();
        }

        // Now return a ColorExtractor instance
        return new Palette(quantizer.getQuantizedColors());
    
public static android.os.AsyncTaskgenerateAsync(android.graphics.Bitmap bitmap, android.support.v7.graphics.Palette$PaletteAsyncListener listener)
Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)} will be called with the created instance. The resulting {@link Palette} is the same as what would be created by calling {@link #generate(Bitmap)}.

param
listener Listener to be invoked when the {@link Palette} has been generated.
return
the {@link android.os.AsyncTask} used to asynchronously generate the instance.

        return generateAsync(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS, listener);
    
public static android.os.AsyncTaskgenerateAsync(android.graphics.Bitmap bitmap, int numColors, android.support.v7.graphics.Palette$PaletteAsyncListener listener)
Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)} will be called with the created instance. The resulting {@link Palette} is the same as what would be created by calling {@link #generate(Bitmap, int)}.

param
listener Listener to be invoked when the {@link Palette} has been generated.
return
the {@link android.os.AsyncTask} used to asynchronously generate the instance.

        checkBitmapParam(bitmap);
        checkNumberColorsParam(numColors);
        checkAsyncListenerParam(listener);

        return AsyncTaskCompat.executeParallel(
                new AsyncTask<Bitmap, Void, Palette>() {
                    @Override
                    protected Palette doInBackground(Bitmap... params) {
                        return generate(params[0], numColors);
                    }

                    @Override
                    protected void onPostExecute(Palette colorExtractor) {
                        listener.onGenerated(colorExtractor);
                    }
                }, bitmap);
    
private voidgenerateEmptySwatches()
Try and generate any missing swatches from the swatches we did find.

        if (mVibrantSwatch == null) {
            // If we do not have a vibrant color...
            if (mDarkVibrantSwatch != null) {
                // ...but we do have a dark vibrant, generate the value by modifying the luma
                final float[] newHsl = copyHslValues(mDarkVibrantSwatch);
                newHsl[2] = TARGET_NORMAL_LUMA;
                mVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0);
            }
        }

        if (mDarkVibrantSwatch == null) {
            // If we do not have a dark vibrant color...
            if (mVibrantSwatch != null) {
                // ...but we do have a vibrant, generate the value by modifying the luma
                final float[] newHsl = copyHslValues(mVibrantSwatch);
                newHsl[2] = TARGET_DARK_LUMA;
                mDarkVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0);
            }
        }
    
public intgetDarkMutedColor(int defaultColor)
Returns a muted and dark color from the palette as an RGB packed int.

param
defaultColor value to return if the swatch isn't available

        return mDarkMutedSwatch != null ? mDarkMutedSwatch.getRgb() : defaultColor;
    
public android.support.v7.graphics.Palette$SwatchgetDarkMutedSwatch()
Returns a muted and dark swatch from the palette. Might be null.

        return mDarkMutedSwatch;
    
public intgetDarkVibrantColor(int defaultColor)
Returns a dark and vibrant color from the palette as an RGB packed int.

param
defaultColor value to return if the swatch isn't available

        return mDarkVibrantSwatch != null ? mDarkVibrantSwatch.getRgb() : defaultColor;
    
public android.support.v7.graphics.Palette$SwatchgetDarkVibrantSwatch()
Returns a dark and vibrant swatch from the palette. Might be null.

        return mDarkVibrantSwatch;
    
public intgetLightMutedColor(int defaultColor)
Returns a muted and light color from the palette as an RGB packed int.

param
defaultColor value to return if the swatch isn't available

        return mLightMutedColor != null ? mLightMutedColor.getRgb() : defaultColor;
    
public android.support.v7.graphics.Palette$SwatchgetLightMutedSwatch()
Returns a muted and light swatch from the palette. Might be null.

        return mLightMutedColor;
    
public intgetLightVibrantColor(int defaultColor)
Returns a light and vibrant color from the palette as an RGB packed int.

param
defaultColor value to return if the swatch isn't available

        return mLightVibrantSwatch != null ? mLightVibrantSwatch.getRgb() : defaultColor;
    
public android.support.v7.graphics.Palette$SwatchgetLightVibrantSwatch()
Returns a light and vibrant swatch from the palette. Might be null.

        return mLightVibrantSwatch;
    
public intgetMutedColor(int defaultColor)
Returns a muted color from the palette as an RGB packed int.

param
defaultColor value to return if the swatch isn't available

        return mMutedSwatch != null ? mMutedSwatch.getRgb() : defaultColor;
    
public android.support.v7.graphics.Palette$SwatchgetMutedSwatch()
Returns a muted swatch from the palette. Might be null.

        return mMutedSwatch;
    
public java.util.ListgetSwatches()
Returns all of the swatches which make up the palette.

        return Collections.unmodifiableList(mSwatches);
    
public intgetVibrantColor(int defaultColor)
Returns the most vibrant color in the palette as an RGB packed int.

param
defaultColor value to return if the swatch isn't available

        return mVibrantSwatch != null ? mVibrantSwatch.getRgb() : defaultColor;
    
public android.support.v7.graphics.Palette$SwatchgetVibrantSwatch()
Returns the most vibrant swatch in the palette. Might be null.

        return mVibrantSwatch;
    
public inthashCode()

        int result = mSwatches != null ? mSwatches.hashCode() : 0;
        result = 31 * result + (mVibrantSwatch != null ? mVibrantSwatch.hashCode() : 0);
        result = 31 * result + (mMutedSwatch != null ? mMutedSwatch.hashCode() : 0);
        result = 31 * result + (mDarkVibrantSwatch != null ? mDarkVibrantSwatch.hashCode() : 0);
        result = 31 * result + (mDarkMutedSwatch != null ? mDarkMutedSwatch.hashCode() : 0);
        result = 31 * result + (mLightVibrantSwatch != null ? mLightVibrantSwatch.hashCode() : 0);
        result = 31 * result + (mLightMutedColor != null ? mLightMutedColor.hashCode() : 0);
        return result;
    
private static floatinvertDiff(float value, float targetValue)
Returns a value in the range 0-1. 1 is returned when {@code value} equals the {@code targetValue} and then decreases as the absolute difference between {@code value} and {@code targetValue} increases.

param
value the item's value
param
targetValue the value which we desire

        return 1f - Math.abs(value - targetValue);
    
private booleanisAlreadySelected(android.support.v7.graphics.Palette$Swatch swatch)

return
true if we have already selected {@code swatch}

        return mVibrantSwatch == swatch || mDarkVibrantSwatch == swatch ||
                mLightVibrantSwatch == swatch || mMutedSwatch == swatch ||
                mDarkMutedSwatch == swatch || mLightMutedColor == swatch;
    
private static android.graphics.BitmapscaleBitmapDown(android.graphics.Bitmap bitmap)
Scale the bitmap down so that it's smallest dimension is {@value #CALCULATE_BITMAP_MIN_DIMENSION}px. If {@code bitmap} is smaller than this, than it is returned.

        final int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight());

        if (minDimension <= CALCULATE_BITMAP_MIN_DIMENSION) {
            // If the bitmap is small enough already, just return it
            return bitmap;
        }

        final float scaleRatio = CALCULATE_BITMAP_MIN_DIMENSION / (float) minDimension;
        return Bitmap.createScaledBitmap(bitmap,
                Math.round(bitmap.getWidth() * scaleRatio),
                Math.round(bitmap.getHeight() * scaleRatio),
                false);
    
private static floatweightedMean(float values)

        float sum = 0f;
        float sumWeight = 0f;

        for (int i = 0; i < values.length; i += 2) {
            float value = values[i];
            float weight = values[i + 1];

            sum += (value * weight);
            sumWeight += weight;
        }

        return sum / sumWeight;