Tuesday, August 20, 2013

Merge images with PorterDuffXfermode

This exercise demonstrate how to merge two images with Paint of PorterDuffXfermode. You can select various PorterDuff.Mode to merge the images to see the effect.

Merge images with PorterDuffXfermode


package com.test.androidimageprocessing;

import java.io.FileNotFoundException;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.example.androidimageprocessing.R;

public class MainActivity extends Activity {
 
 Button btnLoadImage1, btnLoadImage2;
 TextView textSource1, textSource2;
 Button btnProcessing;
 ImageView imageResult;
 Spinner spinnerMode;
 
 final int RQS_IMAGE1 = 1;
 final int RQS_IMAGE2 = 2;
 
 Uri source1, source2;
 
 String[] arrayModeName = {
   "ADD",
   "CLEAR",  
   "DARKEN",  
   "DST",  
   "DST_ATOP",  
   "DST_IN",  
   "DST_OUT",  
   "DST_OVER",  
   "LIGHTEN",  
   "MULTIPLY",  
   "OVERLAY",   
   "SCREEN",  
   "SRC",  
   "SRC_ATOP",  
   "SRC_IN",  
   "SRC_OUT",  
   "SRC_OVER",  
   "XOR" };
 
 /*
  * To use Mode.ADD and Mode.OVERLAY, android:minSdkVersion 
  * have to be set "11" or higher.
  */
 PorterDuff.Mode[] arrayMode = {
   Mode.ADD,
   Mode.CLEAR,  
   Mode.DARKEN,  
   Mode.DST,  
   Mode.DST_ATOP,  
   Mode.DST_IN,  
   Mode.DST_OUT,  
   Mode.DST_OVER,  
   Mode.LIGHTEN,  
   Mode.MULTIPLY,  
   Mode.OVERLAY,   
   Mode.SCREEN,  
   Mode.SRC,  
   Mode.SRC_ATOP,  
   Mode.SRC_IN,  
   Mode.SRC_OUT,  
   Mode.SRC_OVER,  
   Mode.XOR };

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  btnLoadImage1 = (Button)findViewById(R.id.loadimage1);
  btnLoadImage2 = (Button)findViewById(R.id.loadimage2);
  textSource1 = (TextView)findViewById(R.id.sourceuri1);
  textSource2 = (TextView)findViewById(R.id.sourceuri2);
  btnProcessing = (Button)findViewById(R.id.processing);
  imageResult = (ImageView)findViewById(R.id.result);
  
  spinnerMode = (Spinner)findViewById(R.id.mode);
  ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, 
    android.R.layout.simple_spinner_item, arrayModeName);
  adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  spinnerMode.setAdapter(adapter);
  
  btnLoadImage1.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    Intent intent = new Intent(Intent.ACTION_PICK,
      android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(intent, RQS_IMAGE1);
   }});
  
  btnLoadImage2.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    Intent intent = new Intent(Intent.ACTION_PICK,
      android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(intent, RQS_IMAGE2);
   }});
  
  btnProcessing.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    
    if(source1 != null && source2 != null){
     Bitmap processedBitmap = ProcessingBitmap();
     if(processedBitmap != null){
      imageResult.setImageBitmap(processedBitmap);
      Toast.makeText(getApplicationContext(), 
        "Done", 
        Toast.LENGTH_LONG).show();
     }else{
      Toast.makeText(getApplicationContext(), 
        "Something wrong in processing!", 
        Toast.LENGTH_LONG).show();
     }
    }else{
     Toast.makeText(getApplicationContext(), 
       "Select both image!", 
       Toast.LENGTH_LONG).show();
    }
    
   }});
 }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if(resultCode == RESULT_OK){
   switch (requestCode){
   case RQS_IMAGE1:
    source1 = data.getData();
    textSource1.setText(source1.toString());
    break;
   case RQS_IMAGE2:
    source2 = data.getData();
    textSource2.setText(source2.toString());
    break;
   }
  }
 }

 private Bitmap ProcessingBitmap(){
  Bitmap bm1 = null;
  Bitmap bm2 =  null;
  Bitmap newBitmap = null;
  
  try {
   bm1 = BitmapFactory.decodeStream(
     getContentResolver().openInputStream(source1));
   bm2 = BitmapFactory.decodeStream(
     getContentResolver().openInputStream(source2));
   
   int w;
   if(bm1.getWidth() >= bm2.getWidth()){
    w = bm1.getWidth();
   }else{
    w = bm2.getWidth();
   }
   
   int h;
   if(bm1.getHeight() >= bm2.getHeight()){
    h = bm1.getHeight();
   }else{
    h = bm2.getHeight();
   }
   
   Config config = bm1.getConfig();
   if(config == null){
    config = Bitmap.Config.ARGB_8888;
   }
   
   newBitmap = Bitmap.createBitmap(w, h, config);
   Canvas newCanvas = new Canvas(newBitmap);
   
   newCanvas.drawBitmap(bm1, 0, 0, null);
   
   Paint paint = new Paint();
   
   int selectedPos = spinnerMode.getSelectedItemPosition();
   PorterDuff.Mode selectedMode = arrayMode[selectedPos];
   
   paint.setXfermode(new PorterDuffXfermode(selectedMode));
   newCanvas.drawBitmap(bm2, 0, 0, paint);
   
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  return newBitmap;
 }
 
}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold"
        android:layout_gravity="center_horizontal"
        android:autoLink="web" />

    <Button
        android:id="@+id/loadimage1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Load Image 1" />
    <TextView
        android:id="@+id/sourceuri1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/loadimage2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Load Image 2" />
    <TextView
        android:id="@+id/sourceuri2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Spinner
        android:id="@+id/mode"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/processing"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Processing" />
    <ImageView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
</LinearLayout>


download filesDownload the files.

You can download and try the APK here. (The exercises have not take care the bitmap size, and any resizing. So don't test with big picture, otherwise OutOfMemoryError or "Bitmap too large to be uploaded into a texture" will happen).



more: Something about processing images in Android

1 comment:

Dmitry Prisyazhnyuk said...

android:largeHeap="true"
help use large images ;-)