Thursday, September 19, 2013

Scale bitmap with inDither and inPreferQualityOverSpeed

The exercise "Load scaled bitmap" demonstrate a simple and standard method to scale bitmap. Additionally, we can set inDither and inPreferQualityOverSpeed options to improve quality of the scaled bitmap.
  • inDither:
    If set, the decoder will attempt to dither the decoded image.
    Dithering is a technique used in computer graphics to create the illusion of color depth in images with a limited color palette (color quantization). ~ reference: Wikipedia, Dither in Digital photography and image processing.
  • inPreferQualityOverSpeed (Added in API level 10) : 
    If set, the decoder will try to decode the reconstructed image to a higher quality even at the expense of the decoding speed. Currently the field only affects JPEG decode, in the case of which a more accurate, but slightly slower, IDCT method will be used instead.

In this exercise, we can set options of inDither and inPreferQualityOverSpeed in scaling bitmap. And also display the INACCURACY processing duration of scaling bitmap in millisecond, for reference only.

Scale bitmap with inDither and inPreferQualityOverSpeed

package com.example.androidscalebitmap;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Date;

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

public class MainActivity extends Activity {

 final int RQS_IMAGE1 = 1;

 Button btnLoadImage;
 ToggleButton optBtnInDither, optBtnInPreferQualityOverSpeed;
 TextView textInfo;
 ImageView imageResult;

 Uri source1;
 Date startTime;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  btnLoadImage = (Button) findViewById(R.id.loadimage);
  optBtnInDither = (ToggleButton) findViewById(R.id.optInDither);
  optBtnInPreferQualityOverSpeed = (ToggleButton) findViewById(R.id.optInPreferQualityOverSpeed);
  textInfo = (TextView) findViewById(R.id.info);
  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);
   }
  });
  
  optBtnInDither.setOnCheckedChangeListener(
    optBtnOnCheckedChangeListener);
  optBtnInPreferQualityOverSpeed.setOnCheckedChangeListener(
    optBtnOnCheckedChangeListener);
 }
 
 OnCheckedChangeListener optBtnOnCheckedChangeListener =
   new OnCheckedChangeListener(){

    @Override
    public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
     doScale(source1);
    }};

 @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();
    optBtnInDither.setChecked(false);
    optBtnInPreferQualityOverSpeed.setChecked(false);
    doScale(source1);
    break;
   }
  }
 }
 
 private void doScale(Uri src){
  
  try {
   Bitmap bm = loadScaledBitmap(source1);
   imageResult.setImageBitmap(bm);
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
 }

 private Bitmap loadScaledBitmap(Uri src) throws FileNotFoundException {

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

  Bitmap bm = null;

  if (src != 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;
   
   //set options according to RadioButton setting
   options.inDither = optBtnInDither.isChecked();
   //inPreferQualityOverSpeed require API Level 10
   options.inPreferQualityOverSpeed = optBtnInPreferQualityOverSpeed.isChecked();
   
   InputStream inputStream = getContentResolver().openInputStream(src);
   startTime = new Date();
   
   bm = BitmapFactory.decodeStream(inputStream, null, options);
   
   //INACCURACY processing duration of scaling bitmap in millisecond
   long duration = new Date().getTime() - startTime.getTime();
   textInfo.setText("duration in ms: " + duration);
  }

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

  return inSampleSize;
 }
}


<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" />
    <ToggleButton 
        android:id="@+id/optInDither"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:checked="false"
        android:textOn="inDither ON"
        android:textOff="inDither OFF"/>
    <ToggleButton 
        android:id="@+id/optInPreferQualityOverSpeed"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:checked="false"
        android:textOn="inPreferQualityOverSpeed ON"
        android:textOff="inPreferQualityOverSpeed OFF"/>
    <TextView
        android:id="@+id/info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

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

</LinearLayout>


download filesDownload the files.


download filesDownload and try the APK.



more: Something about processing images in Android

No comments: