I’m producing a UI for Android which has a hand-drawn feel to it. To preserve this effect on disabled components, I don’t simply want to globally modify their transparency — it’s not very realistic. Instead, a better approach is to simulate the way that the dark parts of drawings usually are more opaque by generating a mask based on intensity. See my previous post for an example of the effect.
Android makes this really easy by providing a ColorMatrixColorFilter.
/**
* Mask a (typically opaque) source bitmap according to the intensity of its pixels.
*
* @param source the image to be
* @param color a fixed color used to render the source image,
* or zero use the source colors
* @param invert true if lighter pixels are more transparent,
* false if the converse is true
* @param target optional bitmap in which the result will be stored,
* passing in null will generate an ARGB bitmap of the same dimensions
* @return a bitmap containing the masked source
*/
private Bitmap toTransparency(final Bitmap source, final int color, boolean invert, Bitmap target) {
//cache the values we know we will need
final int srcW = source.getWidth();
final int srcH = source.getHeight();
//generate the color matrix coefficients
final float[] coefficients;
if (color == 0) {
final float m = invert ? -0.333333f : 0.333333f;
final float ac = invert ? 256f : 0f;
coefficients = new float[] {
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
m, m, m, 0, ac,
};
} else {
//(these rgb assignments could be tidied away)
final int a = (color >> 24) & 0xff;
final int r = (color >> 16) & 0xff;
final int g = (color >> 8) & 0xff;
final int b = (color ) & 0xff;
final float m = a/255f * (invert ? -0.333333f : 0.333333f);
final float ac = a * (invert ? 1f : 0f);
coefficients = new float[] {
0, 0, 0, 0, r,
0, 0, 0, 0, g,
0, 0, 0, 0, b,
m, m, m, 0, ac,
};
}
//create a suitable target if none specified
final boolean sameSize;
if (target == null) {
target = Bitmap.createBitmap(srcW, srcH, Bitmap.Config.ARGB_8888);
sameSize = true;
} else {
sameSize = target.getWidth() == srcW && target.getHeight() == srcH;
}
//prepare our canvas and paint
//(everything here could be cached in some way)
final Canvas canvas = new Canvas(target);
final Paint paint = new Paint();
paint.setFilterBitmap(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
paint.setColorFilter(new ColorMatrixColorFilter(coefficients));
//do conversion of the source bitmap
if (sameSize) {
canvas.drawBitmap(source, 0, 0, paint);
} else {
Rect src = new Rect(0, 0, srcW, srcH);
Rect dst = new Rect(0, 0, target.getWidth(), target.getHeight());
canvas.drawBitmap(source, src, dst, paint);
}
//return the result
return target;
}