Palette.java revision c6cdc41397bc3ad2c936069af6d448f242790513
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 96c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private final List<Swatch> mSwatches; 97059178a8c7cc80848e5594a9287be91bd924831aChris Banes private final int mHighestPopulation; 98059178a8c7cc80848e5594a9287be91bd924831aChris Banes 99c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mVibrantSwatch; 100c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mMutedSwatch; 101059178a8c7cc80848e5594a9287be91bd924831aChris Banes 102c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mDarkVibrantSwatch; 103c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mDarkMutedSwatch; 104059178a8c7cc80848e5594a9287be91bd924831aChris Banes 105c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mLightVibrantSwatch; 106c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch mLightMutedColor; 107059178a8c7cc80848e5594a9287be91bd924831aChris Banes 108059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 109059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Generate a {@link Palette} from a {@link Bitmap} using the default number of colors. 110059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 111059178a8c7cc80848e5594a9287be91bd924831aChris Banes public static Palette generate(Bitmap bitmap) { 112059178a8c7cc80848e5594a9287be91bd924831aChris Banes return generate(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS); 113059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 114059178a8c7cc80848e5594a9287be91bd924831aChris Banes 115059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 116059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Generate a {@link Palette} from a {@link Bitmap} using the specified {@code numColors}. 117059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Good values for {@code numColors} depend on the source image type. 118059178a8c7cc80848e5594a9287be91bd924831aChris Banes * For landscapes, a good values are in the range 12-16. For images which are largely made up 119059178a8c7cc80848e5594a9287be91bd924831aChris Banes * of people's faces then this value should be increased to 24-32. 120059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 121059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param numColors The maximum number of colors in the generated palette. Increasing this 122059178a8c7cc80848e5594a9287be91bd924831aChris Banes * number will increase the time needed to compute the values. 123059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 124059178a8c7cc80848e5594a9287be91bd924831aChris Banes public static Palette generate(Bitmap bitmap, int numColors) { 125c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkBitmapParam(bitmap); 126c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkNumberColorsParam(numColors); 127059178a8c7cc80848e5594a9287be91bd924831aChris Banes 128059178a8c7cc80848e5594a9287be91bd924831aChris Banes // First we'll scale down the bitmap so it's shortest dimension is 100px 129059178a8c7cc80848e5594a9287be91bd924831aChris Banes final Bitmap scaledBitmap = scaleBitmapDown(bitmap); 130059178a8c7cc80848e5594a9287be91bd924831aChris Banes 131059178a8c7cc80848e5594a9287be91bd924831aChris Banes // Now generate a quantizer from the Bitmap 132059178a8c7cc80848e5594a9287be91bd924831aChris Banes ColorCutQuantizer quantizer = ColorCutQuantizer.fromBitmap(scaledBitmap, numColors); 133059178a8c7cc80848e5594a9287be91bd924831aChris Banes 134059178a8c7cc80848e5594a9287be91bd924831aChris Banes // If created a new bitmap, recycle it 135059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (scaledBitmap != bitmap) { 136059178a8c7cc80848e5594a9287be91bd924831aChris Banes scaledBitmap.recycle(); 137059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 138059178a8c7cc80848e5594a9287be91bd924831aChris Banes 139059178a8c7cc80848e5594a9287be91bd924831aChris Banes // Now return a ColorExtractor instance 140059178a8c7cc80848e5594a9287be91bd924831aChris Banes return new Palette(quantizer.getQuantizedColors()); 141059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 142059178a8c7cc80848e5594a9287be91bd924831aChris Banes 143059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 144059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)} 145059178a8c7cc80848e5594a9287be91bd924831aChris Banes * will be called with the created instance. The resulting {@link Palette} is the same as 146059178a8c7cc80848e5594a9287be91bd924831aChris Banes * what would be created by calling {@link #generate(Bitmap)}. 147059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 148059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param listener Listener to be invoked when the {@link Palette} has been generated. 149059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 150059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @return the {@link android.os.AsyncTask} used to asynchronously generate the instance. 151059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 152c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public static AsyncTask<Bitmap, Void, Palette> generateAsync( 153059178a8c7cc80848e5594a9287be91bd924831aChris Banes Bitmap bitmap, PaletteAsyncListener listener) { 154059178a8c7cc80848e5594a9287be91bd924831aChris Banes return generateAsync(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS, listener); 155059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 156059178a8c7cc80848e5594a9287be91bd924831aChris Banes 157059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 158059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)} 159059178a8c7cc80848e5594a9287be91bd924831aChris Banes * will be called with the created instance. The resulting {@link Palette} is the same as what 160059178a8c7cc80848e5594a9287be91bd924831aChris Banes * would be created by calling {@link #generate(Bitmap, int)}. 161059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 162059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param listener Listener to be invoked when the {@link Palette} has been generated. 163059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 164059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @return the {@link android.os.AsyncTask} used to asynchronously generate the instance. 165059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 166c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public static AsyncTask<Bitmap, Void, Palette> generateAsync( 167059178a8c7cc80848e5594a9287be91bd924831aChris Banes final Bitmap bitmap, final int numColors, final PaletteAsyncListener listener) { 168c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkBitmapParam(bitmap); 169c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkNumberColorsParam(numColors); 170c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes checkAsyncListenerParam(listener); 171c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 172c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return AsyncTaskCompat.executeParallel( 173c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes new AsyncTask<Bitmap, Void, Palette>() { 174c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes @Override 175c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes protected Palette doInBackground(Bitmap... params) { 176c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return generate(params[0], numColors); 177c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 178c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 179c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes @Override 180c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes protected void onPostExecute(Palette colorExtractor) { 181c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes listener.onGenerated(colorExtractor); 182c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 183c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes }, bitmap); 184059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 185059178a8c7cc80848e5594a9287be91bd924831aChris Banes 186c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Palette(List<Swatch> swatches) { 187c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mSwatches = swatches; 188059178a8c7cc80848e5594a9287be91bd924831aChris Banes mHighestPopulation = findMaxPopulation(); 189059178a8c7cc80848e5594a9287be91bd924831aChris Banes 190c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mVibrantSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA, 191059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); 192059178a8c7cc80848e5594a9287be91bd924831aChris Banes 193c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mLightVibrantSwatch = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f, 194059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); 195059178a8c7cc80848e5594a9287be91bd924831aChris Banes 196c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mDarkVibrantSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA, 197059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); 198059178a8c7cc80848e5594a9287be91bd924831aChris Banes 199c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mMutedSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA, 200059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); 201059178a8c7cc80848e5594a9287be91bd924831aChris Banes 202059178a8c7cc80848e5594a9287be91bd924831aChris Banes mLightMutedColor = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f, 203059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); 204059178a8c7cc80848e5594a9287be91bd924831aChris Banes 205c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mDarkMutedSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA, 206059178a8c7cc80848e5594a9287be91bd924831aChris Banes TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); 207059178a8c7cc80848e5594a9287be91bd924831aChris Banes 208059178a8c7cc80848e5594a9287be91bd924831aChris Banes // Now try and generate any missing colors 209c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes generateEmptySwatches(); 210059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 211059178a8c7cc80848e5594a9287be91bd924831aChris Banes 212059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 213c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns all of the swatches which make up the palette. 214059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 215c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public List<Swatch> getSwatches() { 216c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return Collections.unmodifiableList(mSwatches); 217059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 218059178a8c7cc80848e5594a9287be91bd924831aChris Banes 219059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 220c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns the most vibrant swatch in the palette. Might be null. 221059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 222c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getVibrantSwatch() { 223c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mVibrantSwatch; 224059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 225059178a8c7cc80848e5594a9287be91bd924831aChris Banes 226059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 227c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a light and vibrant swatch from the palette. Might be null. 228059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 229c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getLightVibrantSwatch() { 230c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mLightVibrantSwatch; 231059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 232059178a8c7cc80848e5594a9287be91bd924831aChris Banes 233059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 234c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a dark and vibrant swatch from the palette. Might be null. 235059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 236c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getDarkVibrantSwatch() { 237c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mDarkVibrantSwatch; 238059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 239059178a8c7cc80848e5594a9287be91bd924831aChris Banes 240059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 241c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted swatch from the palette. Might be null. 242059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 243c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getMutedSwatch() { 244c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mMutedSwatch; 245059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 246059178a8c7cc80848e5594a9287be91bd924831aChris Banes 247059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 248c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted and light swatch from the palette. Might be null. 249059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 250c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getLightMutedSwatch() { 251059178a8c7cc80848e5594a9287be91bd924831aChris Banes return mLightMutedColor; 252059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 253059178a8c7cc80848e5594a9287be91bd924831aChris Banes 254059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 255c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted and dark swatch from the palette. Might be null. 256c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 257c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public Swatch getDarkMutedSwatch() { 258c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mDarkMutedSwatch; 259c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 260c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 261c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 262c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns the most vibrant color in the palette as an RGB packed int. 263c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 264c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 265c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 266c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getVibrantColor(int defaultColor) { 267c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mVibrantSwatch != null ? mVibrantSwatch.getRgb() : defaultColor; 268c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 269c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 270c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 271c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a light and vibrant color from the palette as an RGB packed int. 272c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 273c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 274c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 275c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getLightVibrantColor(int defaultColor) { 276c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mLightVibrantSwatch != null ? mLightVibrantSwatch.getRgb() : defaultColor; 277c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 278c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 279c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 280c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a dark and vibrant color from the palette as an RGB packed int. 281c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 282c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 283c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 284c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getDarkVibrantColor(int defaultColor) { 285c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mDarkVibrantSwatch != null ? mDarkVibrantSwatch.getRgb() : defaultColor; 286c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 287c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 288c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 289c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted color from the palette as an RGB packed int. 290c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 291c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 292c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 293c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getMutedColor(int defaultColor) { 294c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mMutedSwatch != null ? mMutedSwatch.getRgb() : defaultColor; 295c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 296c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 297c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 298c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted and light color from the palette as an RGB packed int. 299c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 300c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 301c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 302c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getLightMutedColor(int defaultColor) { 303c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mLightMutedColor != null ? mLightMutedColor.getRgb() : defaultColor; 304c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 305c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 306c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 307c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Returns a muted and dark color from the palette as an RGB packed int. 308c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * 309c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @param defaultColor value to return if the swatch isn't available 310059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 311c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getDarkMutedColor(int defaultColor) { 312c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mDarkMutedSwatch != null ? mDarkMutedSwatch.getRgb() : defaultColor; 313059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 314059178a8c7cc80848e5594a9287be91bd924831aChris Banes 315059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 316c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @return true if we have already selected {@code swatch} 317059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 318c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private boolean isAlreadySelected(Swatch swatch) { 319c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mVibrantSwatch == swatch || mDarkVibrantSwatch == swatch || 320c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mLightVibrantSwatch == swatch || mMutedSwatch == swatch || 321c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mDarkMutedSwatch == swatch || mLightMutedColor == swatch; 322059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 323059178a8c7cc80848e5594a9287be91bd924831aChris Banes 324c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private Swatch findColor(float targetLuma, float minLuma, float maxLuma, 325c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes float targetSaturation, float minSaturation, float maxSaturation) { 326c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes Swatch max = null; 327059178a8c7cc80848e5594a9287be91bd924831aChris Banes float maxValue = 0f; 328059178a8c7cc80848e5594a9287be91bd924831aChris Banes 329c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes for (Swatch swatch : mSwatches) { 330c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final float sat = swatch.getHsl()[1]; 331c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final float luma = swatch.getHsl()[2]; 332059178a8c7cc80848e5594a9287be91bd924831aChris Banes 333059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (sat >= minSaturation && sat <= maxSaturation && 334059178a8c7cc80848e5594a9287be91bd924831aChris Banes luma >= minLuma && luma <= maxLuma && 335c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes !isAlreadySelected(swatch)) { 336059178a8c7cc80848e5594a9287be91bd924831aChris Banes float thisValue = createComparisonValue(sat, targetSaturation, luma, targetLuma, 337c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes swatch.getPopulation(), mHighestPopulation); 338059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (max == null || thisValue > maxValue) { 339c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes max = swatch; 340059178a8c7cc80848e5594a9287be91bd924831aChris Banes maxValue = thisValue; 341059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 342059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 343059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 344059178a8c7cc80848e5594a9287be91bd924831aChris Banes 345059178a8c7cc80848e5594a9287be91bd924831aChris Banes return max; 346059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 347059178a8c7cc80848e5594a9287be91bd924831aChris Banes 348059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 349c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Try and generate any missing swatches from the swatches we did find. 350059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 351c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private void generateEmptySwatches() { 352c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mVibrantSwatch == null) { 353059178a8c7cc80848e5594a9287be91bd924831aChris Banes // If we do not have a vibrant color... 354c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mDarkVibrantSwatch != null) { 355059178a8c7cc80848e5594a9287be91bd924831aChris Banes // ...but we do have a dark vibrant, generate the value by modifying the luma 356c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final float[] newHsl = copyHslValues(mDarkVibrantSwatch); 357059178a8c7cc80848e5594a9287be91bd924831aChris Banes newHsl[2] = TARGET_NORMAL_LUMA; 358c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0); 359059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 360059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 361059178a8c7cc80848e5594a9287be91bd924831aChris Banes 362c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mDarkVibrantSwatch == null) { 363059178a8c7cc80848e5594a9287be91bd924831aChris Banes // If we do not have a dark vibrant color... 364c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mVibrantSwatch != null) { 365059178a8c7cc80848e5594a9287be91bd924831aChris Banes // ...but we do have a vibrant, generate the value by modifying the luma 366c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final float[] newHsl = copyHslValues(mVibrantSwatch); 367059178a8c7cc80848e5594a9287be91bd924831aChris Banes newHsl[2] = TARGET_DARK_LUMA; 368c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mDarkVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0); 369059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 370059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 371059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 372059178a8c7cc80848e5594a9287be91bd924831aChris Banes 373059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 374c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Find the {@link Swatch} with the highest population value and return the population. 375059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 376059178a8c7cc80848e5594a9287be91bd924831aChris Banes private int findMaxPopulation() { 377059178a8c7cc80848e5594a9287be91bd924831aChris Banes int population = 0; 378c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes for (Swatch swatch : mSwatches) { 379c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes population = Math.max(population, swatch.getPopulation()); 380059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 381059178a8c7cc80848e5594a9287be91bd924831aChris Banes return population; 382059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 383059178a8c7cc80848e5594a9287be91bd924831aChris Banes 384059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 385059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Scale the bitmap down so that it's smallest dimension is 386059178a8c7cc80848e5594a9287be91bd924831aChris Banes * {@value #CALCULATE_BITMAP_MIN_DIMENSION}px. If {@code bitmap} is smaller than this, than it 387059178a8c7cc80848e5594a9287be91bd924831aChris Banes * is returned. 388059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 389059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static Bitmap scaleBitmapDown(Bitmap bitmap) { 390059178a8c7cc80848e5594a9287be91bd924831aChris Banes final int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight()); 391059178a8c7cc80848e5594a9287be91bd924831aChris Banes 392059178a8c7cc80848e5594a9287be91bd924831aChris Banes if (minDimension <= CALCULATE_BITMAP_MIN_DIMENSION) { 393059178a8c7cc80848e5594a9287be91bd924831aChris Banes // If the bitmap is small enough already, just return it 394059178a8c7cc80848e5594a9287be91bd924831aChris Banes return bitmap; 395059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 396059178a8c7cc80848e5594a9287be91bd924831aChris Banes 397059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float scaleRatio = CALCULATE_BITMAP_MIN_DIMENSION / (float) minDimension; 398059178a8c7cc80848e5594a9287be91bd924831aChris Banes return Bitmap.createScaledBitmap(bitmap, 399059178a8c7cc80848e5594a9287be91bd924831aChris Banes Math.round(bitmap.getWidth() * scaleRatio), 400059178a8c7cc80848e5594a9287be91bd924831aChris Banes Math.round(bitmap.getHeight() * scaleRatio), 401059178a8c7cc80848e5594a9287be91bd924831aChris Banes false); 402059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 403059178a8c7cc80848e5594a9287be91bd924831aChris Banes 404059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static float createComparisonValue(float saturation, float targetSaturation, 405059178a8c7cc80848e5594a9287be91bd924831aChris Banes float luma, float targetLuma, 406059178a8c7cc80848e5594a9287be91bd924831aChris Banes int population, int highestPopulation) { 407059178a8c7cc80848e5594a9287be91bd924831aChris Banes return weightedMean( 408c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes invertDiff(saturation, targetSaturation), WEIGHT_SATURATION, 409c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes invertDiff(luma, targetLuma), WEIGHT_LUMA, 410c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes population / (float) highestPopulation, WEIGHT_POPULATION 411059178a8c7cc80848e5594a9287be91bd924831aChris Banes ); 412059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 413059178a8c7cc80848e5594a9287be91bd924831aChris Banes 414059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 415c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Copy a {@link Swatch}'s HSL values into a new float[]. 416059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 417c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static float[] copyHslValues(Swatch color) { 418059178a8c7cc80848e5594a9287be91bd924831aChris Banes final float[] newHsl = new float[3]; 419059178a8c7cc80848e5594a9287be91bd924831aChris Banes System.arraycopy(color.getHsl(), 0, newHsl, 0, 3); 420059178a8c7cc80848e5594a9287be91bd924831aChris Banes return newHsl; 421059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 422059178a8c7cc80848e5594a9287be91bd924831aChris Banes 423059178a8c7cc80848e5594a9287be91bd924831aChris Banes /** 424059178a8c7cc80848e5594a9287be91bd924831aChris Banes * Returns a value in the range 0-1. 1 is returned when {@code value} equals the 425059178a8c7cc80848e5594a9287be91bd924831aChris Banes * {@code targetValue} and then decreases as the absolute difference between {@code value} and 426059178a8c7cc80848e5594a9287be91bd924831aChris Banes * {@code targetValue} increases. 427059178a8c7cc80848e5594a9287be91bd924831aChris Banes * 428059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param value the item's value 429059178a8c7cc80848e5594a9287be91bd924831aChris Banes * @param targetValue the value which we desire 430059178a8c7cc80848e5594a9287be91bd924831aChris Banes */ 431059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static float invertDiff(float value, float targetValue) { 432059178a8c7cc80848e5594a9287be91bd924831aChris Banes return 1f - Math.abs(value - targetValue); 433059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 434059178a8c7cc80848e5594a9287be91bd924831aChris Banes 435059178a8c7cc80848e5594a9287be91bd924831aChris Banes private static float weightedMean(float... values) { 436059178a8c7cc80848e5594a9287be91bd924831aChris Banes float sum = 0f; 437059178a8c7cc80848e5594a9287be91bd924831aChris Banes float sumWeight = 0f; 438059178a8c7cc80848e5594a9287be91bd924831aChris Banes 439059178a8c7cc80848e5594a9287be91bd924831aChris Banes for (int i = 0; i < values.length; i += 2) { 440059178a8c7cc80848e5594a9287be91bd924831aChris Banes float value = values[i]; 441059178a8c7cc80848e5594a9287be91bd924831aChris Banes float weight = values[i + 1]; 442059178a8c7cc80848e5594a9287be91bd924831aChris Banes 443059178a8c7cc80848e5594a9287be91bd924831aChris Banes sum += (value * weight); 444059178a8c7cc80848e5594a9287be91bd924831aChris Banes sumWeight += weight; 445059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 446059178a8c7cc80848e5594a9287be91bd924831aChris Banes 447059178a8c7cc80848e5594a9287be91bd924831aChris Banes return sum / sumWeight; 448059178a8c7cc80848e5594a9287be91bd924831aChris Banes } 449059178a8c7cc80848e5594a9287be91bd924831aChris Banes 450c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static void checkBitmapParam(Bitmap bitmap) { 451c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (bitmap == null) { 452c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes throw new IllegalArgumentException("bitmap can not be null"); 453c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 454c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (bitmap.isRecycled()) { 455c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes throw new IllegalArgumentException("bitmap can not be recycled"); 456c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 457c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 458c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 459c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static void checkNumberColorsParam(int numColors) { 460c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (numColors < 1) { 461c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes throw new IllegalArgumentException("numColors must be 1 of greater"); 462c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 463c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 464c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 465c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private static void checkAsyncListenerParam(PaletteAsyncListener listener) { 466c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (listener == null) { 467c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes throw new IllegalArgumentException("listener can not be null"); 468c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 469c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 470c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 471c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 472c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Represents a color swatch generated from an image's palette. The RGB color can be retrieved 473c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * by calling {@link #getRgb()}. 474c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 475c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public static final class Swatch { 476c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 477c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final int mRed, mGreen, mBlue; 478c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final int mRgb; 479c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes final int mPopulation; 480c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 481c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes private float[] mHsl; 482c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 483c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes Swatch(int rgbColor, int population) { 484c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mRed = Color.red(rgbColor); 485c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mGreen = Color.green(rgbColor); 486c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mBlue = Color.blue(rgbColor); 487c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mRgb = rgbColor; 488c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mPopulation = population; 489c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 490c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 491c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes Swatch(int red, int green, int blue, int population) { 492c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mRed = red; 493c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mGreen = green; 494c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mBlue = blue; 495c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mRgb = Color.rgb(red, green, blue); 496c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mPopulation = population; 497c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 498c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 499c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 500c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @return this swatch's RGB color value 501c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 502c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getRgb() { 503c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mRgb; 504c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 505c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 506c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 507c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * Return this swatch's HSL values. 508c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * hsv[0] is Hue [0 .. 360) 509c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * hsv[1] is Saturation [0...1] 510c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * hsv[2] is Lightness [0...1] 511c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 512c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public float[] getHsl() { 513c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes if (mHsl == null) { 514c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes // Lazily generate HSL values from RGB 515c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes mHsl = new float[3]; 516c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes ColorUtils.RGBtoHSL(mRed, mGreen, mBlue, mHsl); 517c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 518c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mHsl; 519c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 520c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 521c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes /** 522c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes * @return the number of pixels represented by this swatch 523c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes */ 524c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public int getPopulation() { 525c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return mPopulation; 526c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 527c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 528c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes @Override 529c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes public String toString() { 530c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes return new StringBuilder(getClass().getSimpleName()).append(" ") 531c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes .append("[").append(Integer.toHexString(getRgb())).append(']') 532c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes .append("[HSL: ").append(Arrays.toString(getHsl())).append(']') 533c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes .append("[Population: ").append(mPopulation).append(']').toString(); 534c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 535c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes } 536c6cdc41397bc3ad2c936069af6d448f242790513Chris Banes 537059178a8c7cc80848e5594a9287be91bd924831aChris Banes} 538