Optimizing Per Pixel Blending on Android Bitmaps

2.5k Views Asked by At

I am trying to apply blend filters on two images (in this case HardLight). HardLight is not supported in the base Android library, so, I am doing it by hand on each pixel. The first run is working, but the speed is less than stellar. Generating a 500x500 image from a base 500x500 image and a 500x500 filter is taking way too long. This chunk of code is used to generate thumbnails as well (72x72), and it integral to the core of the application. I'd love some advice and/or hints on how to speed this up.

If huge gains can be made by making the assumption that neither image will have alpha, that is fine. NOTE: BlendMode and alpha are values not used in the example (BlendMode will chose the type of blend, in this case I hardcoded HardLight).

public Bitmap blendedBitmap(Bitmap source, Bitmap layer, BlendMode blendMode, float alpha) {
    Bitmap base = source.copy(Config.ARGB_8888, true);
    Bitmap blend = layer.copy(Config.ARGB_8888, false);

    IntBuffer buffBase = IntBuffer.allocate(base.getWidth() * base.getHeight());
    base.copyPixelsToBuffer(buffBase);
    buffBase.rewind();

    IntBuffer buffBlend = IntBuffer.allocate(blend.getWidth() * blend.getHeight());
    blend.copyPixelsToBuffer(buffBlend);
    buffBlend.rewind();

    IntBuffer buffOut = IntBuffer.allocate(base.getWidth() * base.getHeight());
    buffOut.rewind();

    while (buffOut.position() < buffOut.limit()) {
        int filterInt = buffBlend.get();
        int srcInt = buffBase.get();

        int redValueFilter = Color.red(filterInt);
        int greenValueFilter = Color.green(filterInt);
        int blueValueFilter = Color.blue(filterInt);

        int redValueSrc = Color.red(srcInt);
        int greenValueSrc = Color.green(srcInt);
        int blueValueSrc = Color.blue(srcInt);

        int redValueFinal = hardlight(redValueFilter, redValueSrc);
        int greenValueFinal = hardlight(greenValueFilter, greenValueSrc);
        int blueValueFinal = hardlight(blueValueFilter, blueValueSrc);

        int pixel = Color.argb(255, redValueFinal, greenValueFinal, blueValueFinal);

        buffOut.put(pixel);
    }

    buffOut.rewind();

    base.copyPixelsFromBuffer(buffOut);
    blend.recycle();

    return base;
}

private int hardlight(int in1, int in2) {
    float image = (float)in2;
    float mask = (float)in1;
    return ((int)((image < 128) ? (2 * mask * image / 255):(255 - 2 * (255 - mask) * (255 - image) / 255)));

}
3

There are 3 best solutions below

0
On

There was a question like this a while back (Link). I'm not sure that the OP ever solved his problem.

What I did in my case to achive a "photoshop blend" was by applying a tranlucent shadow overlay to a single color. It was simply a matter of my graphic designer figuring out how to do the shadow overlay. It worked out great and I completely ignored the problem of having to iterate through pixels in a bitmap (I was using getPixels() and setPixels()). The tough part was really on my designers end, but once he figured it out, he's generated a number of great looking images.

I basically used an alpha mask (to generate dynamic colors) with the shadow overlay. I would love to learn about a solution via code, so good luck to you!

Edit: Also, I'm not familiar with BlendMode. You never used it in your method. What is this, a custom class?

3
On

Floating point operations are generally slower than integer, although I can't say anything about Android specifically. I wonder why you are converting the input to hardlight into floating point, when the operations look like they'd work perfectly as integers?

You might also get a speedup by putting the formula inline into the loop, rather than calling a function. Or maybe not, but it's worth trying and benchmarking.

2
On

Also if you can sacrifice 3 bits/pixel of final image quality/precision,- we can win about 25% performance gain in function hardlight(), by rewriting it with bit-wise operators:

int hardlight(int image, int mask) {
    return (((image < 128) ? 
           ( (((mask << 1) * image) >> 8) ):
           (255^( (((255^mask) << 1) * (255^image)) >> 8))));
}