Friday, August 30, 2013

List files in directory with specified type

The example List all 'jpg' files in given directoty:

 private File[] getJpgFiles(File f){
  File[] files = f.listFiles(new FilenameFilter(){

   @Override
   public boolean accept(File dir, String filename) {
    return filename.toLowerCase().endsWith(".jpg");
   }});
  
  return files;
 }


Get bitmap color on touched position in ImageView

Last exercise demonstrate "How to detect touch event and position on ImageView". It's modified to get bitmap color on the touched position.

Get bitmap color on touched position in ImageView


package com.example.androiddrawbitmap;

import java.io.FileNotFoundException;

import android.R.color;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 Button btnLoadImage;
 TextView textSource, textInfo;
 ImageView imageResult;
 
 final int RQS_IMAGE1 = 1;

 Uri source;
 Bitmap bitmapMaster;
 Canvas canvasMaster;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  btnLoadImage = (Button)findViewById(R.id.loadimage);
  textSource = (TextView)findViewById(R.id.sourceuri);
  imageResult = (ImageView)findViewById(R.id.result);
  
  btnLoadImage.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);
   }});
  
  imageResult.setOnTouchListener(new OnTouchListener(){

   @Override
   public boolean onTouch(View v, MotionEvent event) {
    
    int action = event.getAction();
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch(action){
    case MotionEvent.ACTION_DOWN:
     textSource.setText("ACTION_DOWN- " + x + " : " + y);
     textSource.setBackgroundColor(
       getProjectedColor((ImageView)v, bitmapMaster, x, y));
     break;
    case MotionEvent.ACTION_MOVE:
     textSource.setText("ACTION_MOVE- " + x + " : " + y);
     textSource.setBackgroundColor(
       getProjectedColor((ImageView)v, bitmapMaster, x, y));
     break;
    case MotionEvent.ACTION_UP:
     textSource.setText("ACTION_UP- " + x + " : " + y);
     textSource.setBackgroundColor(
       getProjectedColor((ImageView)v, bitmapMaster, x, y));
     break;
    }
    /*
     * Return 'true' to indicate that the event have been consumed.
     * If auto-generated 'false', your code can detect ACTION_DOWN only,
     * cannot detect ACTION_MOVE and ACTION_UP.
     */
    return true;
   }});
 }
 
 /*
  * Project position on ImageView to position on Bitmap
  * return the color on the position 
  */
 private int getProjectedColor(ImageView iv, Bitmap bm, int x, int y){
  if(x<0 || y<0 || x > iv.getWidth() || y > iv.getHeight()){
   //outside ImageView
   return color.background_light; 
  }else{
   int projectedX = (int)((double)x * ((double)bm.getWidth()/(double)iv.getWidth()));
   int projectedY = (int)((double)y * ((double)bm.getHeight()/(double)iv.getHeight()));

   textSource.setText(x + ":" + y + "/" + iv.getWidth() + " : " + iv.getHeight() + "\n" +
     projectedX + " : " + projectedY + "/" + bm.getWidth() + " : " + bm.getHeight()
     );
   
     return bm.getPixel(projectedX, projectedY);
  }
 }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if(resultCode == RESULT_OK){
   switch (requestCode){
   case RQS_IMAGE1:
    source = data.getData();
    textSource.setText(source.toString());
    
    try {
     bitmapMaster = BitmapFactory.decodeStream(
       getContentResolver().openInputStream(source));
     imageResult.setImageBitmap(bitmapMaster);
    } catch (FileNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    
    break;
   }
  }
 }

}


Modify android:scaleType and android:adjustViewBounds of ImageView in activity_main.xml. You can also try different setting to see how it affect the ImageView and Bitmap reloation.
<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:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

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

    <Button
        android:id="@+id/loadimage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Load Image 1" />

    <TextView
        android:id="@+id/sourceuri"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ImageView
        android:id="@+id/result"
        android:scaleType="centerInside"
        android:adjustViewBounds="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/background_dark" />

</LinearLayout>


download filesDownload the files.

Next: Detect touch and free draw on Bitmap


more: Something about processing images in Android

Thursday, August 29, 2013

Detect touch on ImageView

The example demonstrate how to detect touch event and position on ImageView, by implementing OnTouchListener().

Detect touch on ImageView


MainActivity.java
package com.example.androiddrawbitmap;

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.BitmapFactory;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 Button btnLoadImage;
 TextView textSource, textInfo;
 ImageView imageResult;
 
 final int RQS_IMAGE1 = 1;

 Uri source;
 Bitmap bitmapMaster;
 Canvas canvasMaster;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  btnLoadImage = (Button)findViewById(R.id.loadimage);
  textSource = (TextView)findViewById(R.id.sourceuri);
  imageResult = (ImageView)findViewById(R.id.result);
  
  btnLoadImage.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);
   }});
  
  imageResult.setOnTouchListener(new OnTouchListener(){

   @Override
   public boolean onTouch(View v, MotionEvent event) {
    
    int action = event.getAction();
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch(action){
    case MotionEvent.ACTION_DOWN:
     textSource.setText("ACTION_DOWN- " + x + " : " + y);
     break;
    case MotionEvent.ACTION_MOVE:
     textSource.setText("ACTION_MOVE- " + x + " : " + y);
     break;
    case MotionEvent.ACTION_UP:
     textSource.setText("ACTION_UP- " + x + " : " + y);
     break;
    }
    /*
     * Return 'true' to indicate that the event have been consumed.
     * If auto-generated 'false', your code can detect ACTION_DOWN only,
     * cannot detect ACTION_MOVE and ACTION_UP.
     */
    return true;
   }});
 }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if(resultCode == RESULT_OK){
   switch (requestCode){
   case RQS_IMAGE1:
    source = data.getData();
    textSource.setText(source.toString());
    
    try {
     bitmapMaster = BitmapFactory.decodeStream(
       getContentResolver().openInputStream(source));
     imageResult.setImageBitmap(bitmapMaster);
    } catch (FileNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    
    break;
   }
  }
 }

}


Layout
<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:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

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

    <Button
        android:id="@+id/loadimage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Load Image 1" />

    <TextView
        android:id="@+id/sourceuri"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ImageView
        android:id="@+id/result"
        android:scaleType="matrix"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/background_dark" />

</LinearLayout>


download filesDownload the files.

Next: Get bitmap color on touched position in ImageView



more: Something about processing images in Android

Tuesday, August 27, 2013

Android Native Development Kit Cookbook


Android Native Development Kit Cookbook
A step-by-step tutorial with more than 60 concise recipes on Android NDK development skills
Overview
  • Build, debug, and profile Android NDK apps
  • Implement part of Android apps in native C/C++ code.
  • Optimize code performance in assembly with Android NDK.
In Detail
Building Android applications would usually mean that you spend all of your time working in Java. There are however times when this is not the most efficient or best method for the application being built. This is where Android NDK comes in. Android NDK allows the developer to write in Native C/C++, giving you the power to reuse code and libraries and also, in most cases, increase the speed and efficiency of your application.
The "Android Native Development Kit Cookbook" will help you understand the development, building, and debugging of your native Android applications. We will discover and learn JNI programming and essential NDK APIs such as OpenGL ES, and the native application API. We will then explore the process of porting existing libraries and software to NDK. By the end of this book you will be able to build your own apps in NDK apps.
"Android Native Development Kit Cookbook" begins with basic recipes that will help you in the building and debugging of native apps, and JNI programming. The recipes cover various topics of application development with Android NDK such as OpenGL programming and Multimedia programming. We will begin with a simple recipe, Hello NDK, before moving on to cover advanced topics with recipes on OpenGL ES that focus on 2D and 3D graphics, as well as recipes that discuss working with NDK and external APIs. If you are looking for ways to make your application available in Android and take measures to boost your application’s performance, then this Cookbook is for you.
What you will learn from this book
  • Develop Android apps in C/C++ without a single line of Java.
  • Program 2D/3D graphics with both OpenGL ES 1x and 2.0 in Android NDK.
  • Write multi-threaded Android apps in Android NDK.
  • Port existing C/C++ libraries and applications to Android with NDK.
  • Develop multimedia Android apps with Android NDK.
Approach
This book is written in a Cookbook style, beginning with recipes which focus on helping developers make their software/application available in Android.
Who this book is written for
Android developers who want to learn Android NDK programming, or develop multimedia and games in Android NDK will benefit from this book.

Fine tune scaled down bitmap to exact size

Last post genarate a scaled down bitmaps, approximate but not exact size. In order to fine tune bitmap to exact size, we can re-create another bitmap from this scaled bitmap using Bitmap.createScaledBitmap() method with expected width and height.

Scale down bitmap to exact size

Modify MainActivity.java from Last post to fine tune bitmap after scaled.
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));
    */

   bm1 = loadScaledBitmap(source1);
   Toast.makeText(getApplicationContext(),
     "bm1: " + bm1.getWidth() + " x " + bm1.getHeight(),
     Toast.LENGTH_LONG).show();

   bm2 = loadScaledBitmap(source2);
   Toast.makeText(getApplicationContext(),
     "bm2: " + bm2.getWidth() + " x " + bm2.getHeight(),
     Toast.LENGTH_LONG).show();

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

 private Bitmap loadScaledBitmap(Uri src) throws FileNotFoundException {

  // required max width/height
  final int REQ_WIDTH = 1024;
  final int REQ_HEIGHT = 1024;

  Bitmap bm = null;

  // First decode with inJustDecodeBounds=true to check dimensions
  final BitmapFactory.Options options = new BitmapFactory.Options();
  options.inJustDecodeBounds = true;
  BitmapFactory.decodeStream(getContentResolver().openInputStream(src),
    null, options);

  // Calculate inSampleSize
  options.inSampleSize = calculateInSampleSize(options, REQ_WIDTH,
    REQ_HEIGHT);

  // Decode bitmap with inSampleSize set
  options.inJustDecodeBounds = false;
  bm = BitmapFactory.decodeStream(
    getContentResolver().openInputStream(src), null, options);
  
  /*
   * Up to here, bm will be scaled down, but not exactly expected size!
   * 
   * To fine tune the size, re-create another bitmap from this scaled bitmap
   * using Bitmap.createScaledBitmap() method with expected width and height.
   */
  final int LONGEST_LENGTH = 1024;
  int dstWidth, dstHeight;
  
  if(options.outWidth >= options.outHeight){
   //Landscape orientation
   dstWidth = LONGEST_LENGTH;
   dstHeight = LONGEST_LENGTH * options.outHeight/options.outWidth;
  }else{
   //portrait orientation
   dstHeight = LONGEST_LENGTH;
   dstWidth = LONGEST_LENGTH * options.outWidth/options.outHeight;
  }
  
  //To save memory, re-use bm
  bm = Bitmap.createScaledBitmap(bm, dstWidth, dstHeight, false);

  return bm;
 }

 private int calculateInSampleSize(BitmapFactory.Options options,
   int reqWidth, int reqHeight) {
  // Raw height and width of image
  final int height = options.outHeight;
  final int width = options.outWidth;
  int inSampleSize = 1;

  if (height > reqHeight || width > reqWidth) {

   // Calculate ratios of height and width to requested height and
   // width
   final int heightRatio = Math.round((float) height
     / (float) reqHeight);
   final int widthRatio = Math.round((float) width / (float) reqWidth);

   // Choose the smallest ratio as inSampleSize value, this will
   // guarantee
   // a final image with both dimensions larger than or equal to the
   // requested height and width.
   inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
  }

  Toast.makeText(getApplicationContext(),
    "inSampleSize: " + inSampleSize, Toast.LENGTH_LONG).show();

  return inSampleSize;
 }

}


download filesDownload the files.



more: Something about processing images in Android

Monday, August 26, 2013

Load scaled bitmap

Last post provide UN-RECOMMENDED methods to handle error of 'OutOfMemoryError' and 'Bitmap too large to be uploaded into a texture' for images/bitmaps. Here is another approach, scale-down bitmap before processing and display. It's recommanded by Google's document Loading Large Bitmaps Efficiently for displaying bitmap.

Load scaled bitmap


Please note that the final value of BitmapFactory.Options.inSampleSize used by decoder will be based on powers of 2, any other value will be rounded down to the nearest power of 2.

For example: if the original photos is in 4256x2832, and REQ_WIDTH/REQ_HEIGHT are 1024, the calculated inSampleSize will be 3. But the rounded down value used by decoder will be 2. And the scaled bitmap will be 2128x1416.

Refer to the exercise "Merge images with PorterDuffXfermode", modify MainActivity.java to implement loadScaledBitmap() and calculateInSampleSize() methods.

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

   bm1 = loadScaledBitmap(source1);
   Toast.makeText(getApplicationContext(),
     "bm1: " + bm1.getWidth() + " x " + bm1.getHeight(),
     Toast.LENGTH_LONG).show();

   bm2 = loadScaledBitmap(source2);
   Toast.makeText(getApplicationContext(),
     "bm2: " + bm2.getWidth() + " x " + bm2.getHeight(),
     Toast.LENGTH_LONG).show();

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

 private Bitmap loadScaledBitmap(Uri src) throws FileNotFoundException {

  // required max width/height
  final int REQ_WIDTH = 800;
  final int REQ_HEIGHT = 800;

  Bitmap bm = null;

  // First decode with inJustDecodeBounds=true to check dimensions
  final BitmapFactory.Options options = new BitmapFactory.Options();
  options.inJustDecodeBounds = true;
  BitmapFactory.decodeStream(getContentResolver().openInputStream(src),
    null, options);

  // Calculate inSampleSize
  options.inSampleSize = calculateInSampleSize(options, REQ_WIDTH,
    REQ_HEIGHT);

  // Decode bitmap with inSampleSize set
  options.inJustDecodeBounds = false;
  bm = BitmapFactory.decodeStream(
    getContentResolver().openInputStream(src), null, options);

  return bm;
 }

 public int calculateInSampleSize(BitmapFactory.Options options,
   int reqWidth, int reqHeight) {
  // Raw height and width of image
  final int height = options.outHeight;
  final int width = options.outWidth;
  int inSampleSize = 1;

  if (height > reqHeight || width > reqWidth) {

   // Calculate ratios of height and width to requested height and
   // width
   final int heightRatio = Math.round((float) height
     / (float) reqHeight);
   final int widthRatio = Math.round((float) width / (float) reqWidth);

   // Choose the smallest ratio as inSampleSize value, this will
   // guarantee
   // a final image with both dimensions larger than or equal to the
   // requested height and width.
   inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
  }

  Toast.makeText(getApplicationContext(),
    "inSampleSize: " + inSampleSize, Toast.LENGTH_LONG).show();

  return inSampleSize;
 }

}


download filesDownload the files.

Next: Fine tune scaled down bitmap to exact size
Related: Scale bitmap with inDither and inPreferQualityOverSpeed


more: Something about processing images in Android

Sunday, August 25, 2013

Handle error of 'OutOfMemoryError' and 'Bitmap too large to be uploaded into a texture' for images/bitmaps

Before start this post, I have to mention that: IT'S STRONGLY NOT RECOMMENDED TO USE THE APPROACH HERE.

In the previous exercise for image processing, I haven't handle resizing of the images/bitmaps. It will cause the app close, or no display on ImageView, if you try to load with large images. Mainly there are two problem here:

- java.lang.OutOfMemoryError

It's due to Heap Size not enough to handle the bitmaps, you can try to require large heap by defining android:largeHeap="true" inside <application> in AndroidManifest.xml file. In my trial experience, after defined it, the app can handle the original 3264x2448 photos taken by HTC One X, but cannot handle original 4256x2832 photos from Nikon D700 DSLR.

- Bitmap too large to be uploaded into a texture (..., max=2048x2048)

It's due to OpenGLRenderer hardware Acceleration cannot handle bitmap larger than 2048x2048. You can try to disable hardware Acceleration by defining android:hardwareAccelerated="false" inside <application> in AndroidManifest.xml file.

Finally, your AndroidManifest.xml will look like this:

    <application
        ...
        android:hardwareAccelerated="false"
        android:largeHeap="true" >

Exercise of "Merge images with PorterDuffXfermode" with these modification to handle original photos by HTC One X.

Anyway, both method cannot solve the problem perfectly, so it's not recommended.



Sometimes, even you have enough memory but not in single block. For example, you have three 1K memory block in seperated area, but you request memory of 1.5K, you will get 'OutOfMemoryError' also!

Good official lessons teach Caching Bitmaps and Managing Bitmap Memory, it is recommended to read if you need to handle a number of images in your app.


more: Something about processing images in Android

Saturday, August 24, 2013

Google Play Developer Program Policy Update

As of August 23, 2013, the new Google Play Developer Program Policy (“Content Policy”) will be in effect for all new applications submitted to the service. Any pre-existing applications must achieve compliance or be voluntarily unpublished by the developer within 30 days of the issuance of this notification.

https://support.google.com/googleplay/android-developer/answer/3311168

The Google Play Developer Program Policy (“Content Policy”) have been updated. Improvements include new guidance on ads behavior and clarifications to existing policies related to hate speech, gambling, in-app payments, ratings, and impersonation.

Please visit and familiarize yourself with the above policies. If you find any existing applications in your catalog to be in non-compliance, we ask you to remedy and republish the application within 30 calendar days of the posting of this notification. After this time period, applications discovered to be in violation may be subject to removal from Google Play. Any newly published applications must adhere to the latest version of the Content Policy for Google Play.

Friday, August 23, 2013

Share bitmap between Activities

This exercise demonstrate how to using static Bitmap in a common class in application, to share between activities. Such that, no need to pass between activities.




CommonBitmap.java, it simple hold a static Bitmap. It can be accessed by all activities in application, using CommonBitmap.bitmap.
package com.example.androidcommonbitmap;

import android.graphics.Bitmap;

public class CommonBitmap {
 public static Bitmap bitmap = null;
}


MainActivity.java
package com.example.androidcommonbitmap;

import java.io.FileNotFoundException;

import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.app.Activity;
import android.content.Intent;
import android.graphics.BitmapFactory;

public class MainActivity extends Activity {
 
 Button btnLoadImage1;
 TextView textSource1;
 Button btnStartActivity;
 ImageView imageResult;
 
 final int RQS_LOADIMAGE = 1;
 final int RQS_ACTIVITY2 = 2;
 
 Uri source1;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  btnLoadImage1 = (Button)findViewById(R.id.loadimage1);
  textSource1 = (TextView)findViewById(R.id.sourceuri1);
  btnStartActivity = (Button)findViewById(R.id.startactivity);
  imageResult = (ImageView)findViewById(R.id.result);
  
  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_LOADIMAGE);
   }});
  
  btnStartActivity.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    Intent intent = new Intent(MainActivity.this, Activity2.class);
       startActivityForResult(intent, RQS_ACTIVITY2);
   }});
 }
 
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if(resultCode == RESULT_OK){
   switch (requestCode){
   case RQS_LOADIMAGE:
    source1 = data.getData();
    textSource1.setText(source1.toString());
    
    try {
     //save common static bitmap
     CommonBitmap.bitmap = BitmapFactory.decodeStream(
       getContentResolver().openInputStream(source1));
    } catch (FileNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    break;
   case RQS_ACTIVITY2:
    imageResult.setImageBitmap(CommonBitmap.bitmap);
    break;
   }
  }
 }

}


activity_main.xml
<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/startactivity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Another Activity" />
    <ImageView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
</LinearLayout>


Activity2.java, code of the second activity.
package com.example.androidcommonbitmap;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class Activity2 extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity2_layout);
  
  ImageView image2 = (ImageView)findViewById(R.id.image2);
  image2.setImageBitmap(CommonBitmap.bitmap);
  
  Button btnFinish = (Button)findViewById(R.id.finish);
  btnFinish.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    setResult(RESULT_OK);
    finish();
   }});
 }

}


activity2_layout.xml
<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/finish"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Finish" />
    <ImageView
        android:id="@+id/image2"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
</LinearLayout>


Finally, add the second activity Activity2.java to AndroidManifest.xml.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidcommonbitmap"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.androidcommonbitmap.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.example.androidcommonbitmap.Activity2"
            android:label="@string/app_name" >
        </activity>
    </application>

</manifest>


download filesDownload the files.



more: Something about processing images in Android

OpenGL ES 2 for Android: A Quick-Start Guide

OpenGL ES 2 for Android: A Quick-Start Guide


Android is booming like never before, with millions of devices shipping every day. It's never been a better time to learn how to create your own 3D games and live wallpaper for Android. You'll find out all about shaders and the OpenGL pipeline, and discover the power of OpenGL ES 2.0, which is much more feature-rich than its predecessor. If you can program in Java and you have a creative vision that you'd like to share with the world, then this is the book for you.

This book will teach you everything you need to know to create compelling graphics on Android. You'll learn the basics of OpenGL by building a simple game of air hockey, and along the way, you'll see how to initialize OpenGL and program the graphics pipeline using shaders. Each lesson builds upon the one before it, as you add colors, shading, 3D projections, touch interaction, and more.

Then, you'll find out how to turn your idea into a live wallpaper that can run on the home screen. You'll learn about more advanced effects involving particles, lighting models, and the depth buffer. You'll understand what to look for when debugging your program, and what to watch out for when deploying to the market.

OpenGL can be somewhat of a dark art to the uninitiated. As you read this book, you'll learn each new concept from first principles. You won't just learn about a feature; you'll also understand how it works, and why it works the way it does. Everything you learn is forward-compatible with the just-released OpenGL ES 3, and you can even apply these techniques to other platforms, such as iOS or HTML5 WebGL.

Thursday, August 22, 2013

Processing on desktop vs Processing for Android

It's the same code run Processing on desktop and Android, with Android Mode.

Run on Nexus One:

Run on desktop:


The example code:
void setup(){
  size(400, 300);
  background(0);
  stroke(255);
}

void draw() {

  noFill();
  
  if(mousePressed){
    background(0);
    point(mouseX, mouseY);
    line(80, 50, mouseX, mouseY);
    line(50, 200, 350, 250);
    curve(80, 50, mouseX, mouseY, 50, 200, 350, 250);

  }
  
}


Related:
- Setup Processing for Android development
- Hello World of Processing for Android

Cross post: Arduino-er blog - Processing on desktop vs Processing for Android

Example to create shadow frame for image

create shadow frame for image


package com.example.androidframeimage;

import java.io.FileNotFoundException;

import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.RectF;

public class MainActivity extends Activity {

 Button btnLoadImage1;
 TextView textSource1;
 Button btnProcessing;
 ImageView imageResult;
 
 final int RQS_IMAGE1 = 1;
 
 Uri source1;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  btnLoadImage1 = (Button)findViewById(R.id.loadimage1);
  textSource1 = (TextView)findViewById(R.id.sourceuri1);
  btnProcessing = (Button)findViewById(R.id.processing);
  imageResult = (ImageView)findViewById(R.id.result);
  
  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);
   }});
  
  btnProcessing.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    
    if(source1 != 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 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;
   }
  }
 }
 
 private Bitmap ProcessingBitmap(){
  Bitmap bm1 = null;
  Bitmap newBitmap = null;
  Bitmap newShadowBitmap = null;
  
  try {
   bm1 = BitmapFactory.decodeStream(
     getContentResolver().openInputStream(source1));
   
   int w = bm1.getWidth();
   int h = bm1.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.drawColor(Color.BLACK);

   Paint paint = new Paint();
   paint.setColor(Color.WHITE);
   Rect frame = new Rect(
     (int)(w*0.05), 
     (int)(w*0.05), 
     (int)(w*0.95), 
     (int)(h*0.95));
   RectF frameF = new RectF(frame);
   newCanvas.drawRect(frameF, paint);
   paint.setXfermode(new PorterDuffXfermode(Mode.DARKEN));
   newCanvas.drawBitmap(bm1, 0, 0, paint);
   
   /*
    * Create shadow like outer frame
    */
   
   //create BLACK bitmap with same size of the image
   Bitmap bitmapFullGray = Bitmap.createBitmap(w, h, config);
   Canvas canvasFullGray = new Canvas(bitmapFullGray);
   canvasFullGray.drawColor(0xFF808080);
   
   //create bigger bitmap with shadow frame
   int shadowThick = 100;
   int shadowRadius = 50;
   newShadowBitmap = Bitmap.createBitmap(w+shadowThick+shadowRadius, 
     h+shadowThick+shadowRadius, config);
   Canvas newShadowCanvas = new Canvas(newShadowBitmap);
   newShadowCanvas.drawColor(Color.WHITE);
   
   //generate shadow
   Paint paintShadow = new Paint();
   paintShadow.setShadowLayer(shadowRadius, shadowThick, shadowThick, 0xFF000000);
   newShadowCanvas.drawBitmap(bitmapFullGray, 0, 0, paintShadow);
   
   //Place the image
   paintShadow.clearShadowLayer();
   newShadowCanvas.drawBitmap(newBitmap, 0, 0, paintShadow);
   
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  return newShadowBitmap;
 }

}

Layout refer to last exercise "Create frame on Bitmap image".

download filesDownload the files.



more: Something about processing images in Android

Wednesday, August 21, 2013

Create frame on Bitmap image

With PorterDuffXfermode, we can apply frame on bitmap image. By creating bitmap with frame pattern, then merging image using Paint with Xfermode. You can check effects of various PorterDuffXfermode in last exercise.

Bitmap image with frame


package com.example.androidframeimage;

import java.io.FileNotFoundException;

import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.RectF;

public class MainActivity extends Activity {

 Button btnLoadImage1;
 TextView textSource1;
 Button btnProcessing;
 ImageView imageResult;
 
 final int RQS_IMAGE1 = 1;
 
 Uri source1;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  btnLoadImage1 = (Button)findViewById(R.id.loadimage1);
  textSource1 = (TextView)findViewById(R.id.sourceuri1);
  btnProcessing = (Button)findViewById(R.id.processing);
  imageResult = (ImageView)findViewById(R.id.result);
  
  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);
   }});
  
  btnProcessing.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    
    if(source1 != 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 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;
   }
  }
 }
 
 private Bitmap ProcessingBitmap(){
  Bitmap bm1 = null;
  Bitmap newBitmap = null;
  
  try {
   bm1 = BitmapFactory.decodeStream(
     getContentResolver().openInputStream(source1));
   
   int w = bm1.getWidth();
   int h = bm1.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.drawColor(Color.WHITE);

   Paint paint = new Paint();
   paint.setColor(Color.BLACK);
   Rect frame = new Rect(
     (int)(w*0.05), 
     (int)(w*0.05), 
     (int)(w*0.95), 
     (int)(h*0.95));
   RectF frameF = new RectF(frame);
   newCanvas.drawRoundRect(frameF, (float)(w*0.05), (float)(h*0.05), paint);
   
   paint.setXfermode(new PorterDuffXfermode(Mode.SCREEN));
   newCanvas.drawBitmap(bm1, 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/processing"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Create Image Frame" />
    <ImageView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
</LinearLayout>


download filesDownload the files.

next: Create shadow frame for image



more: Something about processing images in Android

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

Monday, August 19, 2013

Place TextView over ImageView

Last exercise how to "Draw text on bitmap". If you only want to display text over image, not draw text on bitmap, you can simple place TextView over ImageView.

Place TextView over ImageView

Layout
<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:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

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

    <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" />

    <EditText
        android:id="@+id/caption"
        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="Draw text on Bitmap" />

    <RelativeLayout
        android:id="@+id/imagelayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <ImageView
            android:id="@+id/result"
            android:layout_width="match_parent"
            android:layout_height="match_parent" 
            android:layout_alignParentTop="true"/>
        <TextView
            android:id="@+id/resulttext"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" 
            android:textSize="30sp"
            android:textStyle="bold"
            android:layout_alignLeft="@id/result"
            android:layout_alignTop="@id/result"
            android:layout_alignRight="@id/result"
            android:layout_alignBottom="@id/result"/>
    </RelativeLayout>

</LinearLayout>


MainActivity Java code:
package com.example.androiddrawtextonbitmap;

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.BitmapFactory;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
 
 Button btnLoadImage1;
 TextView textSource1;
 EditText editTextCaption;
 Button btnProcessing;
 ImageView imageResult;
 TextView textResult;
 
 final int RQS_IMAGE1 = 1;
 
 Uri source1;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  btnLoadImage1 = (Button)findViewById(R.id.loadimage1);
  textSource1 = (TextView)findViewById(R.id.sourceuri1);
  editTextCaption = (EditText)findViewById(R.id.caption);
  btnProcessing = (Button)findViewById(R.id.processing);
  imageResult = (ImageView)findViewById(R.id.result);
  
  textResult = (TextView)findViewById(R.id.resulttext);

  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);
   }});
  
  btnProcessing.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    
    if(source1 != 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;
   }
  }
 }
 
 private Bitmap ProcessingBitmap(){
  Bitmap newBitmap = null;
  
  try {
   newBitmap = BitmapFactory.decodeStream(
     getContentResolver().openInputStream(source1));
   
   String captionString = editTextCaption.getText().toString();
   if(captionString != null){
    
    textResult.setText(captionString);
    
   }else{
    Toast.makeText(getApplicationContext(), 
      "caption empty!", 
      Toast.LENGTH_LONG).show();
   }
   
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  return newBitmap;
 }

}

download filesDownload the files.

Friday, August 16, 2013

Display Spinner inside PopupWindow

It's a example to display Spinner in PopupWindow. For simple example of using Android PopupWindow, refer to the post.

Display Spinner inside PopupWindow

Display Spinner inside PopupWindow


Create /res/layout/popup.xml to define the view of the PopupWindow. Add <Spinner> with android:spinnerMode="dialog".
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/background_light"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="1dp"
        android:background="@android:color/darker_gray"
        android:orientation="vertical" >
     >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:orientation="vertical" >

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="It's a PopupWindow" />

            <ImageView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:src="@drawable/ic_launcher" />

            <Spinner
                android:id="@+id/popupspinner"
                android:spinnerMode="dialog"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />

            <Button
                android:id="@+id/dismiss"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="Dismiss" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>


Main activity Java code to handle the PopupWindow and Spinner.
package com.example.androidpopupwindow;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.PopupWindow;
import android.widget.Spinner;
import android.app.Activity;

public class MainActivity extends Activity {
 
 String[] DayOfWeek = {"Sunday", "Monday", "Tuesday", 
   "Wednesday", "Thursday", "Friday", "Saturday"};

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  final Button btnOpenPopup = (Button) findViewById(R.id.openpopup);
  btnOpenPopup.setOnClickListener(new Button.OnClickListener() {

   @Override
   public void onClick(View arg0) {
    LayoutInflater layoutInflater = 
      (LayoutInflater)getBaseContext()
      .getSystemService(LAYOUT_INFLATER_SERVICE);
    View popupView = layoutInflater.inflate(R.layout.popup, null);
    final PopupWindow popupWindow = new PopupWindow(
      popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    
    Button btnDismiss = (Button)popupView.findViewById(R.id.dismiss);
    
    Spinner popupSpinner = (Spinner)popupView.findViewById(R.id.popupspinner);
    
    ArrayAdapter<String> adapter = 
      new ArrayAdapter<String>(MainActivity.this, 
        android.R.layout.simple_spinner_item, DayOfWeek);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    popupSpinner.setAdapter(adapter);
    
    btnDismiss.setOnClickListener(new Button.OnClickListener(){

     @Override
     public void onClick(View v) {
      popupWindow.dismiss();
     }});
    
    popupWindow.showAsDropDown(btnOpenPopup, 50, -30);
   }

  });
 }

}


Layout file.
<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:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    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/openpopup"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Open Popup Window" />

</LinearLayout>


download filesDownload the files.


Example to draw Bitmap with ShadowLayer

In the below screen capture, the background bitmap is draw normally, the upper left bitmap of Rubber Duck is draw using Paint with ShadowLayer, the middle one is the shadow layer of the Rubber Duck bitmap.

Example to draw Bitmap with ShadowLayer


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.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
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;
 
 final int RQS_IMAGE1 = 1;
 final int RQS_IMAGE2 = 2;
 
 Uri source1, source2;

 @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);
  
  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();
   paint.setShadowLayer(50.0f, 200.0f, 100.0f, 0xFF000000);
   newCanvas.drawBitmap(bm2, 0, 0, paint);
   
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  return newBitmap;
 }
 
}


The layout file refer to the post "Combine bitmap side-by-side".


download filesDownload the files.



more: Something about processing images in Android