15

I'm beating my head against a wall here, and I'm fairly certain I'm doing something stupid, so time to make my stupidity public.

I'm trying to take two images, blend them together into a third image using standard blending algorithms (Hardlight, softlight, overlay, multiply, etc).

Because Android does not have such blend properties build in, I've gone down the path of taking each pixel and combine them using an algorithm. However, the results are garbage. Below is the results of a simple multiply blend (images used, and expected result).

BASE: alt text

BLEND: alt text

EXPECTED RESULT: alt text

GARBAGE RESULT: alt text

Any help would be appreciated. Below is the code, which I've tried to strip out all the "junk", but some may have made it through. I'll clean it up if something isn't clear.

    ImageView imageView = (ImageView) findViewById(R.id.ImageView01);
    Bitmap base = BitmapFactory.decodeResource(getResources(), R.drawable.base);
    Bitmap result = base.copy(Bitmap.Config.RGB_565, true);
    Bitmap blend = BitmapFactory.decodeResource(getResources(), R.drawable.blend);

    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 = multiply(redValueFilter, redValueSrc);
        int greenValueFinal = multiply(greenValueFilter, greenValueSrc);
        int blueValueFinal = multiply(blueValueFilter, blueValueSrc);

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

        buffOut.put(pixel);
    }

    buffOut.rewind();

    result.copyPixelsFromBuffer(buffOut);   

    BitmapDrawable drawable = new BitmapDrawable(getResources(), result);
    imageView.setImageDrawable(drawable);
}

int multiply(int in1, int in2) {
    return in1 * in2 / 255;
}
MarkPowell
  • 16,482
  • 7
  • 61
  • 77

2 Answers2

15

After reproducing, I think your issue has to do with manipulating the images in RGB565 mode. As discussed in this post, Bitmaps apparently need to be in ARGB8888 mode to manipulate properly. I first got the expected result on a multiply blend by doing the following:

Resources res = getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap base = BitmapFactory.decodeResource(res, R.drawable.base, options);
Bitmap blend = BitmapFactory.decodeResource(res, R.drawable.blend, options);

// now base and blend are in ARGB8888 mode, which is what you want

Bitmap result = base.copy(Config.ARGB_8888, true);
// Continue with IntBuffers as before...

Converting the Bitmaps to ARGB8888 mode did seem to work for me, at least with the gradient test patterns. However, it you only need to do Screen or Multiply, you might try this as well:

// Same image creation/reading as above, then:
Paint p = new Paint();
p.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
p.setShader(new BitmapShader(blend, TileMode.CLAMP, TileMode.CLAMP));

Canvas c = new Canvas();
c.setBitmap(result);
c.drawBitmap(base, 0, 0, null);
c.drawRect(0, 0, base.getWidth(), base.getHeight(), p);

With that, you aren't doing the per-pixel calculations, but you are limited to the preset PorterDuff.Modes. In my quick (and dirty) testing, this was the only way I was able to get the blending to work on non-gradient images.

Sergii Rudchenko
  • 5,170
  • 2
  • 28
  • 24
Kevin Dion
  • 4,027
  • 1
  • 17
  • 13
  • Kevin, thanks for the help. I do not use images with transparency, so opacity would always be 1. I've updated the example with images, and cleaned up code. – MarkPowell Jan 06 '11 at 16:47
  • Hmm thanks for the images - that really is a garbage result. I don't have access to an Android dev environment at the moment, but I'm wondering if it doesn't have something to do with the pixel density of the Bitmaps - it looks like your Bitmaps are using RGB565 color space, but it seems from the docs that Color works in ARGB4444 space. I haven't parsed through all of the Bitmap/Color docs to see how/if this conversion is handled automatically, but it might be worth checking out until I can do some testing on the actual platform. – Kevin Dion Jan 06 '11 at 18:28
  • Alright, I've played with it a bit and I was able to reproduce your 'garbage' result, and I got it to work two different ways. I'll try to change my original answer to reflect this. – Kevin Dion Jan 07 '11 at 02:27
  • Thank you so much Kevin, this really clarified it for me. Regarding PorterDuff, I'd love to do it that way. However, I basically need to reproduce a number of photoshop layers (Overlay, HardLight, SoftLight, etc). – MarkPowell Jan 07 '11 at 17:59
5

Simple overlay you can do this way (for simplicity it is supposed that bmp1 is equal or bigger than bmp2):

private Bitmap bitmapOverlay(Bitmap bmp1, Bitmap bmp2) 
{ 
    Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth(), bmp1.getHeight(), bmp1.getConfig()); 
    Canvas canvas = new Canvas(bmOverlay); 
    canvas.drawBitmap(bmp1, 0, 0, null);
    canvas.drawBitmap(bmp2, 0, 0, null);
    return bmOverlay; 
} 

For more complex blending algorithms, maybe you can help yourself with some available Bitmap/Canvas functions.

Zelimir
  • 11,008
  • 6
  • 50
  • 45
  • Thanks, but it was going down that route and another discussion that led me to believe that complex blending was not possible via the Canvas. This resulted in me attempting per pixel blending. – MarkPowell Jan 05 '11 at 17:37