5

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

}
MarkPowell
  • 16,482
  • 7
  • 61
  • 77

3 Answers3

2

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.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Trying out different things, I was able to get about a 40% performance increase by using int arrays instead of Buffers (using `getPixels()` and `setPixels()`), and not converting the values to floats in `hardlight`. Other than that, I think the best performance you will get is to take that calculation to the native level, maybe implement a custom Xfermode. – Kevin Dion Jan 12 '11 at 19:28
  • hmm, switching to getPixels()/setPixels() I saw a double in calculation time, unless I am misunderstanding what you are saying? – MarkPowell Jan 14 '11 at 14:10
  • @MarkPowell, I think @Kevin was arguing in favor of int arrays, and arguing *against* getPixels/setPixels. You'll need to direct your comment at him by starting it with @Kevin if you have more questions. – Mark Ransom Jan 14 '11 at 14:49
1

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))));
}
Agnius Vasiliauskas
  • 10,935
  • 5
  • 50
  • 70
  • Thanks, seeing about a 10% overall speed improvement with this. However, going to have to see if the 3 bits/pixel sacrifice is doable. – MarkPowell Jan 14 '11 at 14:33
  • Ok. It's a pity that performance gain is only 10% instead of 25% as in my windows machine. I suspect that Android bitwise operators are less optimized than compared to windows platform / MS VS C/C++ compiler. Or maybe main performance bottleneck is not in the hardlight function but somewhere else. – Agnius Vasiliauskas Jan 14 '11 at 18:33
0

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?

Community
  • 1
  • 1
user432209
  • 20,007
  • 10
  • 56
  • 75