Latest Tweets

 

Generating Intensity Image Masks on Android

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;
}
blog comments powered by Disqus