Finding the Dominant Color of an Image in an Android @Drawable

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);`

How to extract the most prominent colour from an image?

There is a nice API which allows you to easily do this called palette. It allows you to get a selection of colors from a Bitmap that you provide, like so:

Palette palette = Palette.generate(myBitmap);
int vibrant = palette.getVibrantColor(0x000000);
int vibrantLight = palette.getLightVibrantColor(0x000000);
int vibrantDark = palette.getDarkVibrantColor(0x000000);
int muted = palette.getMutedColor(0x000000);
int mutedLight = palette.getLightMutedColor(0x000000);
int mutedDark = palette.getDarkMutedColor(0x000000);

The dependency is 'com.android.support:palette-v7:21.0.0'

I think this will perfectly suite your needs. Click here for a full guide on how to implement it.

Pick main color from picture

I probably think you got a fix but for future searches to this question, I suggest you check Pallete Generator by the flutter team.
I will try and give a simple explanation of how the code works but for a detailed example head over to the plugin's GitHub repo.

The example below is going to take an image then select the dominant colors from it and then display the colors

First, we add the required imports

import 'package:palette_generator/palette_generator.dart';

After that let's create the main application class.

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
...
home: const HomePage(
title: 'Colors from image',
image: AssetImage('assets/images/artwork_default.png',),
imageSize: Size(256.0, 170.0),
...

),
);
}
}

In the image field above, place the image that you want to extract the dominant colors from, i used the image shown here.

Next, we create the HomePage class

@immutable
class HomePage extends StatefulWidget {
/// Creates the home page.
const HomePage({
Key key,
this.title,
this.image,
this.imageSize,
}) : super(key: key);

final String title; //App title
final ImageProvider image; //Image provider to load the colors from
final Size imageSize; //Image dimensions

@override
_HomePageState createState() {
return _HomePageState();
}
}

Lets create the _HomePageState too

class _HomePageState extends State<HomePage> {
Rect region;
PaletteGenerator paletteGenerator;

final GlobalKey imageKey = GlobalKey();

@override
void initState() {
super.initState();
region = Offset.zero & widget.imageSize;
_updatePaletteGenerator(region);
}

Future<void> _updatePaletteGenerator(Rect newRegion) async {
paletteGenerator = await PaletteGenerator.fromImageProvider(
widget.image,
size: widget.imageSize,
region: newRegion,
maximumColorCount: 20,
);
setState(() {});
}

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: _kBackgroundColor,
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new AspectRatio(
aspectRatio: 15 / 15,
child: Image(
key: imageKey,
image: widget.image,
),
),
Expanded(child: Swatches(generator: paletteGenerator)),
],
),
);
}
}

The code above just lays out the image and the Swatches which is a class defined below. In initState, we first select a region which the colors will be derived from which in our case is the whole image.

After that we create a class Swatches which receives a PalleteGenerator and draws the swatches for it.

class Swatches extends StatelessWidget {

const Swatches({Key key, this.generator}) : super(key: key);

// The PaletteGenerator that contains all of the swatches that we're going
// to display.
final PaletteGenerator generator;

@override
Widget build(BuildContext context) {
final List<Widget> swatches = <Widget>[];
//The generator field can be null, if so, we return an empty container
if (generator == null || generator.colors.isEmpty) {
return Container();
}
//Loop through the colors in the PaletteGenerator and add them to the list of swatches above
for (Color color in generator.colors) {
swatches.add(PaletteSwatch(color: color));
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
//All the colors,
Wrap(
children: swatches,
),
//The colors with ranking
Container(height: 30.0),
PaletteSwatch(label: 'Dominant', color: generator.dominantColor?.color),
PaletteSwatch(
label: 'Light Vibrant', color: generator.lightVibrantColor?.color),
PaletteSwatch(label: 'Vibrant', color: generator.vibrantColor?.color),
PaletteSwatch(
label: 'Dark Vibrant', color: generator.darkVibrantColor?.color),
PaletteSwatch(
label: 'Light Muted', color: generator.lightMutedColor?.color),
PaletteSwatch(label: 'Muted', color: generator.mutedColor?.color),
PaletteSwatch(
label: 'Dark Muted', color: generator.darkMutedColor?.color),
],
);
}
}

After that lets create a PaletteSwatch class. A palette swatch is just a square of color with an optional label

@immutable
class PaletteSwatch extends StatelessWidget {
// Creates a PaletteSwatch.
//
// If the [color] argument is omitted, then the swatch will show a
// placeholder instead, to indicate that there is no color.
const PaletteSwatch({
Key key,
this.color,
this.label,
}) : super(key: key);

// The color of the swatch. May be null.
final Color color;

// The optional label to display next to the swatch.
final String label;

@override
Widget build(BuildContext context) {
// Compute the "distance" of the color swatch and the background color
// so that we can put a border around those color swatches that are too
// close to the background's saturation and lightness. We ignore hue for
// the comparison.
final HSLColor hslColor = HSLColor.fromColor(color ?? Colors.transparent);
final HSLColor backgroundAsHsl = HSLColor.fromColor(_kBackgroundColor);
final double colorDistance = math.sqrt(
math.pow(hslColor.saturation - backgroundAsHsl.saturation, 2.0) +
math.pow(hslColor.lightness - backgroundAsHsl.lightness, 2.0));

Widget swatch = Padding(
padding: const EdgeInsets.all(2.0),
child: color == null
? const Placeholder(
fallbackWidth: 34.0,
fallbackHeight: 20.0,
color: Color(0xff404040),
strokeWidth: 2.0,
)
: Container(
decoration: BoxDecoration(
color: color,
border: Border.all(
width: 1.0,
color: _kPlaceholderColor,
style: colorDistance < 0.2
? BorderStyle.solid
: BorderStyle.none,
)),
width: 34.0,
height: 20.0,
),
);

if (label != null) {
swatch = ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 130.0, minWidth: 130.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
swatch,
Container(width: 5.0),
Text(label),
],
),
);
}
return swatch;
}
}

Hope this helps, thank you.

How to make a Blur background depends on the Image color that you are loading in Android

You will need to get the dominant color from the image you are using then create a gradient drawable between a starting color and your dominant color. There are multiple ways to find dominant colors which you can read up on here: Finding the dominant color of an image in an Android @drawable

From there you create a drawable and set the background of your view to that drawable:

    // get the drawable from the image view
// could also be pulled from resources if available
Bitmap bm=((BitmapDrawable)imageView.getDrawable()).getBitmap();

int color = getDominantColor(bm);

GradientDrawable gradient = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[] {0xFFF,color});
gradient.setCornerRadius(15f);

contentView.setBackground(gradient);

Adding a color filter to a Drawable changes all Buttons using the same Drawable

Example that should work for you:

Drawable buttonBackground = context.getResources().getDrawable(R.drawable.bg);
buttonBackground = buttonBackground.mutate();

//Set your filter here

How to change color of status bar dynamically in android based upon the foreground image?

It's called Pallete, use the below function, just pass your bitmap image

  private void setUpPalette(Bitmap bitmap) {
// you passed your Bitmap image;
Palette.from(bitmap).
generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
if (palette != null) {

//default color is yellow
// set the color to toolbar, whatever
int extColor = palette.getVibrantColor(ContextCompat.getColor(MainActivity.this, R.color.yellow));
if (getWindow() != null) {
getWindow().setStatusBarColor(ContextCompat.getColor(this, extColor));
}
} else {
if (getWindow() != null) {
getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.fail_safe));
}
}
}
});
}


Related Topics



Leave a reply



Submit