Blending two images together with multiply and % opacity

15.5k views Asked by At

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.

4

There are 4 answers

2
Marcelo Assis On BEST ANSWER

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

0
anas abubakar On

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;
}
2
Den Drobiazko On

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)

0
Basant On

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