1059178a8c7cc80848e5594a9287be91bd924831aChris Banes/* 2059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Copyright 2014 The Android Open Source Project 3059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 4059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5059178a8c7cc80848e5594a9287be91bd924831aChris Banes * you may not use this file except in compliance with the License. 6059178a8c7cc80848e5594a9287be91bd924831aChris Banes * You may obtain a copy of the License at 7059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 8059178a8c7cc80848e5594a9287be91bd924831aChris Banes * http://www.apache.org/licenses/LICENSE-2.0 9059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 10059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Unless required by applicable law or agreed to in writing, software 11059178a8c7cc80848e5594a9287be91bd924831aChris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12059178a8c7cc80848e5594a9287be91bd924831aChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13059178a8c7cc80848e5594a9287be91bd924831aChris Banes * See the License for the specific language governing permissions and 14059178a8c7cc80848e5594a9287be91bd924831aChris Banes * limitations under the License. 15059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 16059178a8c7cc80848e5594a9287be91bd924831aChris Banes 17059178a8c7cc80848e5594a9287be91bd924831aChris Banespackage android.support.v7.graphics; 18059178a8c7cc80848e5594a9287be91bd924831aChris Banes 19059178a8c7cc80848e5594a9287be91bd924831aChris Banesimport android.graphics.Bitmap; 20c6cdc41397bc3ad2c936069af6d448f242790513Chris Banesimport android.graphics.Color; 21059178a8c7cc80848e5594a9287be91bd924831aChris Banesimport android.os.AsyncTask; 22c6cdc41397bc3ad2c936069af6d448f242790513Chris Banesimport android.support.v4.os.AsyncTaskCompat; 23059178a8c7cc80848e5594a9287be91bd924831aChris Banes 24c6cdc41397bc3ad2c936069af6d448f242790513Chris Banesimport java.util.Arrays; 25059178a8c7cc80848e5594a9287be91bd924831aChris Banesimport java.util.Collections; 26059178a8c7cc80848e5594a9287be91bd924831aChris Banesimport java.util.List; 27059178a8c7cc80848e5594a9287be91bd924831aChris Banes 28059178a8c7cc80848e5594a9287be91bd924831aChris Banes/** 29059178a8c7cc80848e5594a9287be91bd924831aChris Banes * A helper class to extract prominent colors from an image. 30059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <p> 31059178a8c7cc80848e5594a9287be91bd924831aChris Banes * A number of colors with different profiles are extracted from the image: 32059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <ul> 33059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <li>Vibrant</li> 34059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <li>Vibrant Dark</li> 35059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <li>Vibrant Light</li> 36059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <li>Muted</li> 37059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <li>Muted Dark</li> 38059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <li>Muted Light</li> 39059178a8c7cc80848e5594a9287be91bd924831aChris Banes * </ul> 40059178a8c7cc80848e5594a9287be91bd924831aChris Banes * These can be retrieved from the appropriate getter method. 41059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 42059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <p> 43059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Instances can be created with the synchronous factory methods {@link #generate(Bitmap)} and 44059178a8c7cc80848e5594a9287be91bd924831aChris Banes * {@link #generate(Bitmap, int)}. 45059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <p> 46059178a8c7cc80848e5594a9287be91bd924831aChris Banes * These should be called on a background thread, ideally the one in 47059178a8c7cc80848e5594a9287be91bd924831aChris Banes * which you load your images on. Sometimes that is not possible, so asynchronous factory methods 48059178a8c7cc80848e5594a9287be91bd924831aChris Banes * have also been provided: {@link #generateAsync(Bitmap, PaletteAsyncListener)} and 49059178a8c7cc80848e5594a9287be91bd924831aChris Banes * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}. These can be used as so: 50059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 51059178a8c7cc80848e5594a9287be91bd924831aChris Banes * <pre> 52059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() { 53059178a8c7cc80848e5594a9287be91bd924831aChris Banes * public void onGenerated(Palette palette) { 54059178a8c7cc80848e5594a9287be91bd924831aChris Banes * // Do something with colors... 55059178a8c7cc80848e5594a9287be91bd924831aChris Banes * } 56059178a8c7cc80848e5594a9287be91bd924831aChris Banes * }); 57059178a8c7cc80848e5594a9287be91bd924831aChris Banes * </pre> 58059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 59059178a8c7cc80848e5594a9287be91bd924831aChris Banespublic final class Palette { 60059178a8c7cc80848e5594a9287be91bd924831aChris Banes 61059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 62059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Listener to be used with {@link #generateAsync(Bitmap, PaletteAsyncListener)} or 63059178a8c7cc80848e5594a9287be91bd924831aChris Banes * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)} 64059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 65059178a8c7cc80848e5594a9287be91bd924831aChris Banes public interface PaletteAsyncListener { 66059178a8c7cc80848e5594a9287be91bd924831aChris Banes 67059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 68059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Called when the {@link Palette} has been generated. 69059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 70059178a8c7cc80848e5594a9287be91bd924831aChris Banes void onGenerated(Palette palette); 71059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 72059178a8c7cc80848e5594a9287be91bd924831aChris Banes 73059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final int CALCULATE_BITMAP_MIN_DIMENSION = 100; 74059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16; 75059178a8c7cc80848e5594a9287be91bd924831aChris Banes 76059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float TARGET_DARK_LUMA = 0.26f; 77059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float MAX_DARK_LUMA = 0.45f; 78059178a8c7cc80848e5594a9287be91bd924831aChris Banes 79059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float MIN_LIGHT_LUMA = 0.55f; 80059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float TARGET_LIGHT_LUMA = 0.74f; 81059178a8c7cc80848e5594a9287be91bd924831aChris Banes 82059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float MIN_NORMAL_LUMA = 0.3f; 83059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float TARGET_NORMAL_LUMA = 0.5f; 84059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float MAX_NORMAL_LUMA = 0.7f; 85059178a8c7cc80848e5594a9287be91bd924831aChris Banes 86059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float TARGET_MUTED_SATURATION = 0.3f; 87059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float MAX_MUTED_SATURATION = 0.4f; 88059178a8c7cc80848e5594a9287be91bd924831aChris Banes 89059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float TARGET_VIBRANT_SATURATION = 1f; 90059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static final float MIN_VIBRANT_SATURATION = 0.35f; 91059178a8c7cc80848e5594a9287be91bd924831aChris Banes 92c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static final float WEIGHT_SATURATION = 3f; 93c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static final float WEIGHT_LUMA = 6f; 94c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static final float WEIGHT_POPULATION = 1f; 95c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 96f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private static final float MIN_CONTRAST_TITLE_TEXT = 3.0f; 97f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private static final float MIN_CONTRAST_BODY_TEXT = 4.5f; 98f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 99c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private final List<Swatch> mSwatches; 100059178a8c7cc80848e5594a9287be91bd924831aChris Banes private final int mHighestPopulation; 101059178a8c7cc80848e5594a9287be91bd924831aChris Banes 102c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mVibrantSwatch; 103c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mMutedSwatch; 104059178a8c7cc80848e5594a9287be91bd924831aChris Banes 105c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mDarkVibrantSwatch; 106c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mDarkMutedSwatch; 107059178a8c7cc80848e5594a9287be91bd924831aChris Banes 108c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mLightVibrantSwatch; 109c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mLightMutedColor; 110059178a8c7cc80848e5594a9287be91bd924831aChris Banes 111059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 112059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Generate a {@link Palette} from a {@link Bitmap} using the default number of colors. 113059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 114059178a8c7cc80848e5594a9287be91bd924831aChris Banes public static Palette generate(Bitmap bitmap) { 115059178a8c7cc80848e5594a9287be91bd924831aChris Banes return generate(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS); 116059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 117059178a8c7cc80848e5594a9287be91bd924831aChris Banes 118059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 119059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Generate a {@link Palette} from a {@link Bitmap} using the specified {@code numColors}. 120059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Good values for {@code numColors} depend on the source image type. 121059178a8c7cc80848e5594a9287be91bd924831aChris Banes * For landscapes, a good values are in the range 12-16. For images which are largely made up 122059178a8c7cc80848e5594a9287be91bd924831aChris Banes * of people's faces then this value should be increased to 24-32. 123059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 124059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param numColors The maximum number of colors in the generated palette. Increasing this 125059178a8c7cc80848e5594a9287be91bd924831aChris Banes * number will increase the time needed to compute the values. 126059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 127059178a8c7cc80848e5594a9287be91bd924831aChris Banes public static Palette generate(Bitmap bitmap, int numColors) { 128c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkBitmapParam(bitmap); 129c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkNumberColorsParam(numColors); 130059178a8c7cc80848e5594a9287be91bd924831aChris Banes 131059178a8c7cc80848e5594a9287be91bd924831aChris Banes // First we'll scale down the bitmap so it's shortest dimension is 100px 132059178a8c7cc80848e5594a9287be91bd924831aChris Banes final Bitmap scaledBitmap = scaleBitmapDown(bitmap); 133059178a8c7cc80848e5594a9287be91bd924831aChris Banes 134059178a8c7cc80848e5594a9287be91bd924831aChris Banes // Now generate a quantizer from the Bitmap 135059178a8c7cc80848e5594a9287be91bd924831aChris Banes ColorCutQuantizer quantizer = ColorCutQuantizer.fromBitmap(scaledBitmap, numColors); 136059178a8c7cc80848e5594a9287be91bd924831aChris Banes 137059178a8c7cc80848e5594a9287be91bd924831aChris Banes // If created a new bitmap, recycle it 138059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (scaledBitmap != bitmap) { 139059178a8c7cc80848e5594a9287be91bd924831aChris Banes scaledBitmap.recycle(); 140059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 141059178a8c7cc80848e5594a9287be91bd924831aChris Banes 142059178a8c7cc80848e5594a9287be91bd924831aChris Banes // Now return a ColorExtractor instance 143059178a8c7cc80848e5594a9287be91bd924831aChris Banes return new Palette(quantizer.getQuantizedColors()); 144059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 145059178a8c7cc80848e5594a9287be91bd924831aChris Banes 146059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 147059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)} 148059178a8c7cc80848e5594a9287be91bd924831aChris Banes * will be called with the created instance. The resulting {@link Palette} is the same as 149059178a8c7cc80848e5594a9287be91bd924831aChris Banes * what would be created by calling {@link #generate(Bitmap)}. 150059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 151059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param listener Listener to be invoked when the {@link Palette} has been generated. 152059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 153059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @return the {@link android.os.AsyncTask} used to asynchronously generate the instance. 154059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 155c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public static AsyncTask<Bitmap, Void, Palette> generateAsync( 156059178a8c7cc80848e5594a9287be91bd924831aChris Banes Bitmap bitmap, PaletteAsyncListener listener) { 157059178a8c7cc80848e5594a9287be91bd924831aChris Banes return generateAsync(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS, listener); 158059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 159059178a8c7cc80848e5594a9287be91bd924831aChris Banes 160059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 161059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)} 162059178a8c7cc80848e5594a9287be91bd924831aChris Banes * will be called with the created instance. The resulting {@link Palette} is the same as what 163059178a8c7cc80848e5594a9287be91bd924831aChris Banes * would be created by calling {@link #generate(Bitmap, int)}. 164059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 165059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param listener Listener to be invoked when the {@link Palette} has been generated. 166059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 167059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @return the {@link android.os.AsyncTask} used to asynchronously generate the instance. 168059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 169c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public static AsyncTask<Bitmap, Void, Palette> generateAsync( 170059178a8c7cc80848e5594a9287be91bd924831aChris Banes final Bitmap bitmap, final int numColors, final PaletteAsyncListener listener) { 171c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkBitmapParam(bitmap); 172c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkNumberColorsParam(numColors); 173c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkAsyncListenerParam(listener); 174c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 175c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return AsyncTaskCompat.executeParallel( 176c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes new AsyncTask<Bitmap, Void, Palette>() { 177c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes @Override 178c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes protected Palette doInBackground(Bitmap... params) { 179c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return generate(params[0], numColors); 180c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 181c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 182c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes @Override 183c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes protected void onPostExecute(Palette colorExtractor) { 184c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes listener.onGenerated(colorExtractor); 185c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 186c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes }, bitmap); 187059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 188059178a8c7cc80848e5594a9287be91bd924831aChris Banes 189c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Palette(List<Swatch> swatches) { 190c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mSwatches = swatches; 191059178a8c7cc80848e5594a9287be91bd924831aChris Banes mHighestPopulation = findMaxPopulation(); 192059178a8c7cc80848e5594a9287be91bd924831aChris Banes 193c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mVibrantSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA, 194059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); 195059178a8c7cc80848e5594a9287be91bd924831aChris Banes 196c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mLightVibrantSwatch = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f, 197059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); 198059178a8c7cc80848e5594a9287be91bd924831aChris Banes 199c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mDarkVibrantSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA, 200059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); 201059178a8c7cc80848e5594a9287be91bd924831aChris Banes 202c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mMutedSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA, 203059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); 204059178a8c7cc80848e5594a9287be91bd924831aChris Banes 205059178a8c7cc80848e5594a9287be91bd924831aChris Banes mLightMutedColor = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f, 206059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); 207059178a8c7cc80848e5594a9287be91bd924831aChris Banes 208c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mDarkMutedSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA, 209059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); 210059178a8c7cc80848e5594a9287be91bd924831aChris Banes 211059178a8c7cc80848e5594a9287be91bd924831aChris Banes // Now try and generate any missing colors 212c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes generateEmptySwatches(); 213059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 214059178a8c7cc80848e5594a9287be91bd924831aChris Banes 215059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 216c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns all of the swatches which make up the palette. 217059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 218c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public List<Swatch> getSwatches() { 219c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return Collections.unmodifiableList(mSwatches); 220059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 221059178a8c7cc80848e5594a9287be91bd924831aChris Banes 222059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 223c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns the most vibrant swatch in the palette. Might be null. 224059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 225c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getVibrantSwatch() { 226c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mVibrantSwatch; 227059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 228059178a8c7cc80848e5594a9287be91bd924831aChris Banes 229059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 230c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a light and vibrant swatch from the palette. Might be null. 231059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 232c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getLightVibrantSwatch() { 233c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mLightVibrantSwatch; 234059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 235059178a8c7cc80848e5594a9287be91bd924831aChris Banes 236059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 237c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a dark and vibrant swatch from the palette. Might be null. 238059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 239c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getDarkVibrantSwatch() { 240c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mDarkVibrantSwatch; 241059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 242059178a8c7cc80848e5594a9287be91bd924831aChris Banes 243059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 244c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted swatch from the palette. Might be null. 245059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 246c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getMutedSwatch() { 247c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mMutedSwatch; 248059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 249059178a8c7cc80848e5594a9287be91bd924831aChris Banes 250059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 251c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted and light swatch from the palette. Might be null. 252059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 253c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getLightMutedSwatch() { 254059178a8c7cc80848e5594a9287be91bd924831aChris Banes return mLightMutedColor; 255059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 256059178a8c7cc80848e5594a9287be91bd924831aChris Banes 257059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 258c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted and dark swatch from the palette. Might be null. 259c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 260c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getDarkMutedSwatch() { 261c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mDarkMutedSwatch; 262c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 263c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 264c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 265c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns the most vibrant color in the palette as an RGB packed int. 266c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 267c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 268c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 269c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getVibrantColor(int defaultColor) { 270c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mVibrantSwatch != null ? mVibrantSwatch.getRgb() : defaultColor; 271c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 272c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 273c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 274c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a light and vibrant color from the palette as an RGB packed int. 275c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 276c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 277c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 278c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getLightVibrantColor(int defaultColor) { 279c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mLightVibrantSwatch != null ? mLightVibrantSwatch.getRgb() : defaultColor; 280c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 281c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 282c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 283c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a dark and vibrant color from the palette as an RGB packed int. 284c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 285c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 286c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 287c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getDarkVibrantColor(int defaultColor) { 288c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mDarkVibrantSwatch != null ? mDarkVibrantSwatch.getRgb() : defaultColor; 289c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 290c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 291c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 292c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted color from the palette as an RGB packed int. 293c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 294c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 295c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 296c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getMutedColor(int defaultColor) { 297c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mMutedSwatch != null ? mMutedSwatch.getRgb() : defaultColor; 298c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 299c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 300c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 301c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted and light color from the palette as an RGB packed int. 302c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 303c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 304c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 305c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getLightMutedColor(int defaultColor) { 306c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mLightMutedColor != null ? mLightMutedColor.getRgb() : defaultColor; 307c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 308c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 309c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 310c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted and dark color from the palette as an RGB packed int. 311c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 312c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 313059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 314c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getDarkMutedColor(int defaultColor) { 315c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mDarkMutedSwatch != null ? mDarkMutedSwatch.getRgb() : defaultColor; 316059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 317059178a8c7cc80848e5594a9287be91bd924831aChris Banes 318059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 319c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @return true if we have already selected {@code swatch} 320059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 321c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private boolean isAlreadySelected(Swatch swatch) { 322c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mVibrantSwatch == swatch || mDarkVibrantSwatch == swatch || 323c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mLightVibrantSwatch == swatch || mMutedSwatch == swatch || 324c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mDarkMutedSwatch == swatch || mLightMutedColor == swatch; 325059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 326059178a8c7cc80848e5594a9287be91bd924831aChris Banes 327c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch findColor(float targetLuma, float minLuma, float maxLuma, 328c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes float targetSaturation, float minSaturation, float maxSaturation) { 329c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes Swatch max = null; 330059178a8c7cc80848e5594a9287be91bd924831aChris Banes float maxValue = 0f; 331059178a8c7cc80848e5594a9287be91bd924831aChris Banes 332c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes for (Swatch swatch : mSwatches) { 333c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final float sat = swatch.getHsl()[1]; 334c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final float luma = swatch.getHsl()[2]; 335059178a8c7cc80848e5594a9287be91bd924831aChris Banes 336059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (sat >= minSaturation && sat <= maxSaturation && 337059178a8c7cc80848e5594a9287be91bd924831aChris Banes luma >= minLuma && luma <= maxLuma && 338c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes !isAlreadySelected(swatch)) { 339059178a8c7cc80848e5594a9287be91bd924831aChris Banes float thisValue = createComparisonValue(sat, targetSaturation, luma, targetLuma, 340c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes swatch.getPopulation(), mHighestPopulation); 341059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (max == null || thisValue > maxValue) { 342c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes max = swatch; 343059178a8c7cc80848e5594a9287be91bd924831aChris Banes maxValue = thisValue; 344059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 345059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 346059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 347059178a8c7cc80848e5594a9287be91bd924831aChris Banes 348059178a8c7cc80848e5594a9287be91bd924831aChris Banes return max; 349059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 350059178a8c7cc80848e5594a9287be91bd924831aChris Banes 351059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 352c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Try and generate any missing swatches from the swatches we did find. 353059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 354c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private void generateEmptySwatches() { 355c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mVibrantSwatch == null) { 356059178a8c7cc80848e5594a9287be91bd924831aChris Banes // If we do not have a vibrant color... 357c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mDarkVibrantSwatch != null) { 358059178a8c7cc80848e5594a9287be91bd924831aChris Banes // ...but we do have a dark vibrant, generate the value by modifying the luma 359c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final float[] newHsl = copyHslValues(mDarkVibrantSwatch); 360059178a8c7cc80848e5594a9287be91bd924831aChris Banes newHsl[2] = TARGET_NORMAL_LUMA; 361c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0); 362059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 363059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 364059178a8c7cc80848e5594a9287be91bd924831aChris Banes 365c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mDarkVibrantSwatch == null) { 366059178a8c7cc80848e5594a9287be91bd924831aChris Banes // If we do not have a dark vibrant color... 367c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mVibrantSwatch != null) { 368059178a8c7cc80848e5594a9287be91bd924831aChris Banes // ...but we do have a vibrant, generate the value by modifying the luma 369c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final float[] newHsl = copyHslValues(mVibrantSwatch); 370059178a8c7cc80848e5594a9287be91bd924831aChris Banes newHsl[2] = TARGET_DARK_LUMA; 371c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mDarkVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0); 372059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 373059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 374059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 375059178a8c7cc80848e5594a9287be91bd924831aChris Banes 376059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 377c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Find the {@link Swatch} with the highest population value and return the population. 378059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 379059178a8c7cc80848e5594a9287be91bd924831aChris Banes private int findMaxPopulation() { 380059178a8c7cc80848e5594a9287be91bd924831aChris Banes int population = 0; 381c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes for (Swatch swatch : mSwatches) { 382c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes population = Math.max(population, swatch.getPopulation()); 383059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 384059178a8c7cc80848e5594a9287be91bd924831aChris Banes return population; 385059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 386059178a8c7cc80848e5594a9287be91bd924831aChris Banes 387e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes @Override 388e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes public boolean equals(Object o) { 389e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (this == o) { 390e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return true; 391e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 392e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (o == null || getClass() != o.getClass()) { 393e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return false; 394e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 395e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes 396e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes Palette palette = (Palette) o; 397e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes 398e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (mSwatches != null ? !mSwatches.equals(palette.mSwatches) : palette.mSwatches != null) { 399e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return false; 400e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 401e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (mDarkMutedSwatch != null ? !mDarkMutedSwatch.equals(palette.mDarkMutedSwatch) 402e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes : palette.mDarkMutedSwatch != null) { 403e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return false; 404e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 405e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (mDarkVibrantSwatch != null ? !mDarkVibrantSwatch.equals(palette.mDarkVibrantSwatch) 406e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes : palette.mDarkVibrantSwatch != null) { 407e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return false; 408e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 409e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (mLightMutedColor != null ? !mLightMutedColor.equals(palette.mLightMutedColor) 410e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes : palette.mLightMutedColor != null) { 411e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return false; 412e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 413e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (mLightVibrantSwatch != null ? !mLightVibrantSwatch.equals(palette.mLightVibrantSwatch) 414e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes : palette.mLightVibrantSwatch != null) { 415e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return false; 416e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 417e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (mMutedSwatch != null ? !mMutedSwatch.equals(palette.mMutedSwatch) 418e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes : palette.mMutedSwatch != null) { 419e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return false; 420e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 421e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (mVibrantSwatch != null ? !mVibrantSwatch.equals(palette.mVibrantSwatch) 422e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes : palette.mVibrantSwatch != null) { 423e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return false; 424e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 425e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes 426e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return true; 427e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 428e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes 429e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes @Override 430e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes public int hashCode() { 431e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes int result = mSwatches != null ? mSwatches.hashCode() : 0; 432e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes result = 31 * result + (mVibrantSwatch != null ? mVibrantSwatch.hashCode() : 0); 433e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes result = 31 * result + (mMutedSwatch != null ? mMutedSwatch.hashCode() : 0); 434e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes result = 31 * result + (mDarkVibrantSwatch != null ? mDarkVibrantSwatch.hashCode() : 0); 435e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes result = 31 * result + (mDarkMutedSwatch != null ? mDarkMutedSwatch.hashCode() : 0); 436e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes result = 31 * result + (mLightVibrantSwatch != null ? mLightVibrantSwatch.hashCode() : 0); 437e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes result = 31 * result + (mLightMutedColor != null ? mLightMutedColor.hashCode() : 0); 438e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return result; 439e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 440e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes 441059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 442059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Scale the bitmap down so that it's smallest dimension is 443059178a8c7cc80848e5594a9287be91bd924831aChris Banes * {@value #CALCULATE_BITMAP_MIN_DIMENSION}px. If {@code bitmap} is smaller than this, than it 444059178a8c7cc80848e5594a9287be91bd924831aChris Banes * is returned. 445059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 446059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static Bitmap scaleBitmapDown(Bitmap bitmap) { 447059178a8c7cc80848e5594a9287be91bd924831aChris Banes final int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight()); 448059178a8c7cc80848e5594a9287be91bd924831aChris Banes 449059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (minDimension <= CALCULATE_BITMAP_MIN_DIMENSION) { 450059178a8c7cc80848e5594a9287be91bd924831aChris Banes // If the bitmap is small enough already, just return it 451059178a8c7cc80848e5594a9287be91bd924831aChris Banes return bitmap; 452059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 453059178a8c7cc80848e5594a9287be91bd924831aChris Banes 454059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float scaleRatio = CALCULATE_BITMAP_MIN_DIMENSION / (float) minDimension; 455059178a8c7cc80848e5594a9287be91bd924831aChris Banes return Bitmap.createScaledBitmap(bitmap, 456059178a8c7cc80848e5594a9287be91bd924831aChris Banes Math.round(bitmap.getWidth() * scaleRatio), 457059178a8c7cc80848e5594a9287be91bd924831aChris Banes Math.round(bitmap.getHeight() * scaleRatio), 458059178a8c7cc80848e5594a9287be91bd924831aChris Banes false); 459059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 460059178a8c7cc80848e5594a9287be91bd924831aChris Banes 461059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static float createComparisonValue(float saturation, float targetSaturation, 462059178a8c7cc80848e5594a9287be91bd924831aChris Banes float luma, float targetLuma, 463059178a8c7cc80848e5594a9287be91bd924831aChris Banes int population, int highestPopulation) { 464059178a8c7cc80848e5594a9287be91bd924831aChris Banes return weightedMean( 465c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes invertDiff(saturation, targetSaturation), WEIGHT_SATURATION, 466c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes invertDiff(luma, targetLuma), WEIGHT_LUMA, 467c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes population / (float) highestPopulation, WEIGHT_POPULATION 468059178a8c7cc80848e5594a9287be91bd924831aChris Banes ); 469059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 470059178a8c7cc80848e5594a9287be91bd924831aChris Banes 471059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 472c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Copy a {@link Swatch}'s HSL values into a new float[]. 473059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 474c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static float[] copyHslValues(Swatch color) { 475059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float[] newHsl = new float[3]; 476059178a8c7cc80848e5594a9287be91bd924831aChris Banes System.arraycopy(color.getHsl(), 0, newHsl, 0, 3); 477059178a8c7cc80848e5594a9287be91bd924831aChris Banes return newHsl; 478059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 479059178a8c7cc80848e5594a9287be91bd924831aChris Banes 480059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 481059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Returns a value in the range 0-1. 1 is returned when {@code value} equals the 482059178a8c7cc80848e5594a9287be91bd924831aChris Banes * {@code targetValue} and then decreases as the absolute difference between {@code value} and 483059178a8c7cc80848e5594a9287be91bd924831aChris Banes * {@code targetValue} increases. 484059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 485059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param value the item's value 486059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param targetValue the value which we desire 487059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 488059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static float invertDiff(float value, float targetValue) { 489059178a8c7cc80848e5594a9287be91bd924831aChris Banes return 1f - Math.abs(value - targetValue); 490059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 491059178a8c7cc80848e5594a9287be91bd924831aChris Banes 492059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static float weightedMean(float... values) { 493059178a8c7cc80848e5594a9287be91bd924831aChris Banes float sum = 0f; 494059178a8c7cc80848e5594a9287be91bd924831aChris Banes float sumWeight = 0f; 495059178a8c7cc80848e5594a9287be91bd924831aChris Banes 496059178a8c7cc80848e5594a9287be91bd924831aChris Banes for (int i = 0; i < values.length; i += 2) { 497059178a8c7cc80848e5594a9287be91bd924831aChris Banes float value = values[i]; 498059178a8c7cc80848e5594a9287be91bd924831aChris Banes float weight = values[i + 1]; 499059178a8c7cc80848e5594a9287be91bd924831aChris Banes 500059178a8c7cc80848e5594a9287be91bd924831aChris Banes sum += (value * weight); 501059178a8c7cc80848e5594a9287be91bd924831aChris Banes sumWeight += weight; 502059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 503059178a8c7cc80848e5594a9287be91bd924831aChris Banes 504059178a8c7cc80848e5594a9287be91bd924831aChris Banes return sum / sumWeight; 505059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 506059178a8c7cc80848e5594a9287be91bd924831aChris Banes 507c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static void checkBitmapParam(Bitmap bitmap) { 508c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (bitmap == null) { 509c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes throw new IllegalArgumentException("bitmap can not be null"); 510c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 511c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (bitmap.isRecycled()) { 512c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes throw new IllegalArgumentException("bitmap can not be recycled"); 513c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 514c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 515c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 516c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static void checkNumberColorsParam(int numColors) { 517c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (numColors < 1) { 518c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes throw new IllegalArgumentException("numColors must be 1 of greater"); 519c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 520c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 521c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 522c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static void checkAsyncListenerParam(PaletteAsyncListener listener) { 523c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (listener == null) { 524c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes throw new IllegalArgumentException("listener can not be null"); 525c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 526c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 527c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 528c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 529c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Represents a color swatch generated from an image's palette. The RGB color can be retrieved 530c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * by calling {@link #getRgb()}. 531c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 532c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public static final class Swatch { 533f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private final int mRed, mGreen, mBlue; 534f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private final int mRgb; 535f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private final int mPopulation; 536c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 537f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private boolean mGeneratedTextColors; 538f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private int mTitleTextColor; 539f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private int mBodyTextColor; 540c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 541c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private float[] mHsl; 542c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 543c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes Swatch(int rgbColor, int population) { 544c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mRed = Color.red(rgbColor); 545c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mGreen = Color.green(rgbColor); 546c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mBlue = Color.blue(rgbColor); 547c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mRgb = rgbColor; 548c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mPopulation = population; 549c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 550c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 551c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes Swatch(int red, int green, int blue, int population) { 552c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mRed = red; 553c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mGreen = green; 554c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mBlue = blue; 555c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mRgb = Color.rgb(red, green, blue); 556c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mPopulation = population; 557c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 558c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 559c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 560c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @return this swatch's RGB color value 561c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 562c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getRgb() { 563c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mRgb; 564c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 565c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 566c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 567c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Return this swatch's HSL values. 568c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * hsv[0] is Hue [0 .. 360) 569c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * hsv[1] is Saturation [0...1] 570c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * hsv[2] is Lightness [0...1] 571c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 572c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public float[] getHsl() { 573c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mHsl == null) { 574c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes // Lazily generate HSL values from RGB 575c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mHsl = new float[3]; 576c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes ColorUtils.RGBtoHSL(mRed, mGreen, mBlue, mHsl); 577c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 578c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mHsl; 579c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 580c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 581c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 582c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @return the number of pixels represented by this swatch 583c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 584c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getPopulation() { 585c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mPopulation; 586c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 587c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 588f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes /** 589f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * Returns an appropriate color to use for any 'title' text which is displayed over this 590f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast. 591f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes */ 592f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes public int getTitleTextColor() { 593f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes ensureTextColorsGenerated(); 594f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes return mTitleTextColor; 595f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 596f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 597f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes /** 598f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * Returns an appropriate color to use for any 'body' text which is displayed over this 599f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast. 600f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes */ 601f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes public int getBodyTextColor() { 602f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes ensureTextColorsGenerated(); 603f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes return mBodyTextColor; 604f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 605f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 606f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes private void ensureTextColorsGenerated() { 607f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes if (!mGeneratedTextColors) { 608f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes mTitleTextColor = ColorUtils.getTextColorForBackground(mRgb, 609f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes MIN_CONTRAST_TITLE_TEXT); 610f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes mBodyTextColor = ColorUtils.getTextColorForBackground(mRgb, 611f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes MIN_CONTRAST_BODY_TEXT); 612f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes mGeneratedTextColors = true; 613f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 614f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes } 615f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes 616c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes @Override 617c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public String toString() { 618f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes return new StringBuilder(getClass().getSimpleName()) 619f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']') 620f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']') 621f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes .append(" [Population: ").append(mPopulation).append(']') 622f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes .append(" [Title Text: #").append(Integer.toHexString(mTitleTextColor)).append(']') 623f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes .append(" [Body Text: #").append(Integer.toHexString(mBodyTextColor)).append(']') 624f3082d731b24a0cee4305ee6bba168a11c11f068Chris Banes .toString(); 625c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 626e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes 627e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes @Override 628e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes public boolean equals(Object o) { 629e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (this == o) { 630e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return true; 631e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 632e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes if (o == null || getClass() != o.getClass()) { 633e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return false; 634e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 635e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes 636e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes Swatch swatch = (Swatch) o; 637e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb; 638e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 639e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes 640e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes @Override 641e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes public int hashCode() { 642e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes return 31 * mRgb + mPopulation; 643e538500b3daa25cbcd460167fcc82d61f7b03a3cChris Banes } 644c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 645c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 646059178a8c7cc80848e5594a9287be91bd924831aChris Banes} 647