Tuesday, May 1, 2012

Android loading and Scaling bitmaps


Android provides some methods which directly loads,scales bitmap to the application.But they have their own disadvantages.They are:
  • BitmapFactory.decodeFile(filePath);
    • This method will decode the image from the specified file path.But this causes some problems like 'Skia-->bitmap retuning null' ,which means application is not able to decode the image file due to memory constraints.
  • Bitmap.createScaledBitmap(bitmap,width, height, true);
    • This method will return a scaled bitmap  with the specified width and height. There is a possibility for the method to return null if application heap is not allowing the image to scale to specified dimensions.
This tutorial helps you to load and scale a bitmaps effectively from disk  in android.

The code below first loads and scales image directly using the methods BitmapFactory.decodeFile() and Bitmap.createScaledBitmap(). If image retrieval is failed by using both of the above methods then go for scaling bitmap using BitmapOptions.
 
 
 /**
     *  Scales the Image in originalFilePath to thumbImagePath ,
     *   Saves the scaled Thumbnail Image. 
     * @param originalFilePath    Path where the Original non scaled image exist
     * @param thumbImagePath    Path in which scaled Thumbnail image is saved.
     */
    private void scaleToThumbNail(String originalFilePath,String thumbImagePath) {
       
        FileOutputStream fos = null;
        FileInputStream fis = null;
        boolean isScaled = false;
       
        try{
           
            //   height of image in px which is defined as 90dp in layout. 
            int heightPX = (int)convertDpToPixel(90, _context);
           
            int IMAGE_MAX_SIZE = heightPX;            //px value
           
            Bitmap bitmap = BitmapFactory.decodeFile(originalFilePath);
            int width = 0;
            int height = 0;
            if(bitmap != null)
            {    // bitmap created from file.
                width  = bitmap.getWidth();
                height = bitmap.getHeight();
               
        //    Calculate width and height maintaining aspect ratio.
                if(width < height)
        {   
                    width = (int)(width*((float)IMAGE_MAX_SIZE/height));
                    height = IMAGE_MAX_SIZE;
                   
                }else{
                    height = (int)(height*((float)IMAGE_MAX_SIZE/width));
                    width = IMAGE_MAX_SIZE;
                }
                try{
                    bitmap = Bitmap.createScaledBitmap(bitmap,width, height, true);
                }catch (OutOfMemoryError e) {
            e.printStackTrace();
        }
            }
           
           if(bitmap != null)
            {    // bitmap scaled succesfully
                fos = new FileOutputStream(thumbImagePath);
                isScaled = bitmap.compress(CompressFormat.PNG, 100, fos);
               
               //    Close File OutputStream
                              
            }
           if(!isScaled)
            {    // Unable to scale the image either in createScaledBitmap or  bitmap.compress
               
                //  Caculate the height and width of the decode stream without 
                //  actually decoding into memory.
                BitmapFactory.Options o = new BitmapFactory.Options();
                o.inJustDecodeBounds = true;
                fis = new FileInputStream(originalFilePath);
                BitmapFactory.decodeStream(fis, null, o);
              
               //Close File InputStream here.
              
               
                int scale = 1;
                if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE)
                {    // Calculating scale based in outHeight and outWidth of BitmapOptions.
                    scale = (int)Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE 
                            / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
                }

                //  The actual decoding done here using inSampleSize property.
                //  Apply the above calculated scale value to inSampleSize of 
                //  Bitmap Factory Options.
                BitmapFactory.Options o2 = new BitmapFactory.Options();
                o2.inSampleSize = scale;
                fis = new FileInputStream(originalFilePath);
                bitmap = BitmapFactory.decodeStream(fis, null, o2);
               
                // Write the ThumbNail to thumbImagePath
                fos = new FileOutputStream(thumbImagePath);
                bitmap.compress(CompressFormat.PNG, 0, fos);
            }
         
            isScaled= true;
        }catch (Throwable e) {
            // TODO: handle exception
             e.printStackTrace();
        }finally{
        // close all streams.
        }
           
    }

Converting dip to px code which is used in above code snippet is posted below.

        
 /**
  * This method convets dp unit to equivalent device specific value in pixels. 
  * 
  * @param dp A value in dp(Device independent pixels) unit. Which we need to convert into pixels
  * @param context Context to get resources and device specific display metrics
  * @return A float value to represent Pixels equivalent to dp according to device
  */
 public static float convertDpToPixel(float dp,Context context){
     Resources resources = context.getResources();
     DisplayMetrics metrics = resources.getDisplayMetrics();
     float px = dp * (metrics.densityDpi/160f);
     return px;
 }


Hope this tutorial helps out.Any better ides invited.