Finding the dominant color of an image in an Android @drawable
In Android 5.0 Lollipop, a class was added to help extract useful colors from a Bitmap. The Palette class, found in android.support.v7.graphics, can extract the following colors:
- Vibrant
- Vibrant Dark
- Vibrant Light
- Muted
- Muted Dark
- Muted Light
This Android training page gives all the details you need to use the class (I tried it myself in Android Studio and it was very straightforward): http://developer.android.com/training/material/drawables.html#ColorExtract
To quote:
The Android Support Library r21 and above includes the Palette class, which lets you extract prominent colors from an image. To extract these colors, pass a Bitmap object to the Palette.generate() static method in the background thread where you load your images. If you can't use that thread, call the Palette.generateAsync() method and provide a listener instead.*
You can retrieve the prominent colors from the image using the getter methods in the Palette class, such as Palette.getVibrantColor.
To use the Palette class in your project, add the following Gradle dependency to your app's module:
dependencies { ... implementation 'com.android.support:palette-v7:21.0.+' }
Or if you're using androidx:
implementation 'androidx.palette:palette:1.0.0'
If you need to use generateAsync(), here's how:
Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
public void onGenerated(Palette palette) {
// Do something with colors...
}
});
EDIT: Since the question asks how to extract colors from a drawable resource, you'd first have to convert the drawable to a bitmap to use the technique I've described. Luckily, that is quite simple using BitmapFactory:
Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon_resource);`
This class iterates through a Bitmap and returns the most dominate colour. Feel free to clean up the code where necessary.
public class ImageColour {
String colour;
public ImageColour(Bitmap image) throws Exception {
int height = image.getHeight();
int width = image.getWidth();
Map m = new HashMap();
for(int i=0; i < width ; i++){
for(int j=0; j < height ; j++){
int rgb = image.getPixel(i, j);
int[] rgbArr = getRGBArr(rgb);
if (!isGray(rgbArr)) {
Integer counter = (Integer) m.get(rgb);
if (counter == null)
counter = 0;
counter++;
m.put(rgb, counter);
}
}
}
String colourHex = getMostCommonColour(m);
}
public static String getMostCommonColour(Map map) {
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Comparable) ((Map.Entry) (o1)).getValue())
.compareTo(((Map.Entry) (o2)).getValue());
}
});
Map.Entry me = (Map.Entry )list.get(list.size()-1);
int[] rgb= getRGBArr((Integer)me.getKey());
return Integer.toHexString(rgb[0])+" "+Integer.toHexString(rgb[1])+" "+Integer.toHexString(rgb[2]);
}
public static int[] getRGBArr(int pixel) {
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel) & 0xff;
return new int[]{red,green,blue};
}
public static boolean isGray(int[] rgbArr) {
int rgDiff = rgbArr[0] - rgbArr[1];
int rbDiff = rgbArr[0] - rgbArr[2];
int tolerance = 10;
if (rgDiff > tolerance || rgDiff < -tolerance)
if (rbDiff > tolerance || rbDiff < -tolerance) {
return false;
}
return true;
}
public String returnColour() {
if (colour.length() == 6) {
return colour.replaceAll("\\s", "");
} else {
return "ffffff";
}
}
to get the hex simply call
returnColour();
To find the Dominant / Vibrant / Muted color from an image, use Palette:
import:
implementation 'androidx.palette:palette:1.0.0'
usage:
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image)
Palette.Builder(bitmap).generate { it?.let { palette ->
val dominantColor = palette.getDominantColor(ContextCompat.getColor(context!!, R.color.defaultColor))
// TODO: use dominant color
} }
There is also an another solution, it's more approximative but if you don't want to have long delay for searching color, it can do the job.
public static int getDominantColor(Bitmap bitmap) {
Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, 1, 1, true);
final int color = newBitmap.getPixel(0, 0);
newBitmap.recycle();
return color;
}