12

I'm trying to blend two images together with Android, using a Multiply-like blending mode.

// Prepare -------------------------------

// Create source images
Bitmap img1 = ...
Bitmap img2 = ...

// Create result image
Bitmap result = ...
Canvas canvas = new Canvas();
canvas.setBitmap(result);

// Get proper display reference
BitmapDrawable drawable = new BitmapDrawable(getResources(), result);
ImageView imageView = (ImageView)findViewById(R.id.imageBlend1);
imageView.setImageDrawable(drawable);


// Apply -------------------------------

// Draw base
canvas.drawBitmap(img1, 0, 0, null);

// Draw overlay
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
paint.setShader(new BitmapShader(img2, TileMode.CLAMP, TileMode.CLAMP));

canvas.drawRect(0, 0, img2.getWidth(), img2.getHeight(), paint);

This works, but I don't have control over the "amount" of multiply that is done - it's always a complete multiply transfer. Ideally, a 0% multiply would be the same as the base image (img1) without any change, but a 100% multiply would be the result I get with the code above.

paint.setAlpha() doesn't seem to work for this.

Any other way to set the % opacity of the new 'layer'?

P.S. There are some methods to make multiply work with this I guess (using a LightingColorFilter) by pre-multiplying and offsetting the color to white, but it's very specific to the multiplymode .. I'm trying to find a way to apply the opacity/% thing to all other transfer modes too.

Den Drobiazko
  • 989
  • 1
  • 11
  • 32
zeh
  • 8,471
  • 3
  • 32
  • 50

4 Answers4

3

I was implementing photo filters similar to what iOS app of ours does. They do it something like source bitmap + mask bitmap + blend mode + alpha value. To achieve identical behaviour I just increased the alpha of mask. Here's what my code finally looks:

public static Bitmap blend(Bitmap back, Bitmap front, BlendMethod method, float alpha) {
    if (alpha != 1.0F) {
        front = makeTransparent(front, Math.round(alpha * 255));
    }

    Bitmap.Config config = back.getConfig();
    int width = back.getWidth();
    int height = back.getHeight();

    if (width != front.getWidth() || height != front.getHeight()) {
        Log.e(TAG, "Arrays must be of identical size! Do bitmap scaling prior to blending.");
        return null;
    }

    int[] frontArr = new int[height * width], backArr = new int[height * width], resultArr;

    back.getPixels(backArr, 0, width, 0, 0, width, height);
    front.getPixels(frontArr, 0, width, 0, 0, width, height);

    resultArr = jniBlend(frontArr, backArr, alpha, method.toInt());
    return Bitmap.createBitmap(resultArr, width, height, config);
}

public static Bitmap blend(Bitmap back, Bitmap front, BlendMethod method) {
    return blend(back, front, method, 1F);
}

public static Bitmap makeTransparent(Bitmap src, int value) {
    int width = src.getWidth();
    int height = src.getHeight();
    Bitmap transBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(transBitmap);
    canvas.drawARGB(0, 0, 0, 0);
    // config paint
    final Paint paint = new Paint();
    paint.setAlpha(value);
    canvas.drawBitmap(src, 0, 0, paint);
    return transBitmap;
}

Note that jniBlend is some method I wrote on my own, it behaves like stock PorterDuff modes in Java.

Method makeTransparent is not mine - found it here: (answer from Ruban)

Community
  • 1
  • 1
Den Drobiazko
  • 989
  • 1
  • 11
  • 32
  • 2
    What is BlendMethod??? Please share the complete one otherwise no use. – Mohsin Oct 23 '19 at 03:21
  • @Mohsin If I correctly remember the problem behind this, I think this is now implemented in AndroidSDK. Back in 2015 it was missing some of blending modes – Den Drobiazko Oct 23 '19 at 08:38
3

I needed to do something like that a while ago, and I found this post about Color Channels a lot enlightening. (But I'm afraid this is related to what you described in your "PS")

Link above in archive.org, thanks to @1j01

Marcelo Assis
  • 4,940
  • 3
  • 31
  • 53
  • 1
    Valeu rapaz. But yeah, the article is great, but it's just more about attaching layers with different modes rather than having a way to change % of input values for each mode. It does show some direct manipulation of pixels after reading them from the image, so maybe it's an alternative. I'll need to investigate that. For now, thanks! – zeh Oct 17 '11 at 15:15
  • 1
    Wayback Machine link: https://web.archive.org/web/20150310155646/http://kevindion.com/2011/01/android-bitmap-blending-color-channels/ – 1j01 Apr 10 '18 at 05:36
0

The code is not complete. it is just to give you an idea so that you can use render script to blend an image

public Bitmap blend(Bitmap image)
{
    final float BLUR_RADIUS = 25f;
    Bitmap outbitmap = Bitmap.createBitmap(image);
    final RenderScript renderScript = RenderScript.create(this);

    Allocation tmpin = Allocation.createFromBitmap(renderScript,image);
    Allocation tmpout = Allocation.createFromBitmap(renderScript,outbitmap);
    ScriptIntrinsicBlend blend = ScriptIntrinsicBlend.create(renderScript,
    Element.U8_4(renderScript));

    blend.forEachMultiply(tmpin,tmpout);
    return outbitmap;
}
Jainish Kapadia
  • 2,535
  • 4
  • 14
  • 28
0

I am using these lines of code from @Den Drobiazko answer and its working perfectly for blending bitmap

// pass your Bitmap and percentage of visibility as a integer value

    public static Bitmap makeTransparent(Bitmap src, int value) {
    int width = src.getWidth();
    int height = src.getHeight();
    Bitmap transBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(transBitmap);
    canvas.drawARGB(0, 0, 0, 0);
    // config paint
    Log.e("test","value "+value);
    final Paint paint = new Paint();
    paint.setAlpha(value);
    canvas.drawBitmap(src, 0, 0, paint);
    return transBitmap;
}
Basant
  • 666
  • 6
  • 14