package net.the4thdimension.android; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.media.ExifInterface; import android.net.Uri; import android.os.Build; import android.provider.MediaStore.Images.Media; import android.util.Log; import java.io.FileNotFoundException; import java.io.IOException; /** * Provides helper methods for image operations ***/ public class ImageUtils { private final static String TAG = ImageUtils.class.getSimpleName(); private static final String ERROR_URI_NULL = "Uri cannot be null"; /** * Scales the image depending upon the display density of the device. Maintains image aspect * ratio. * <p> * When dealing with the bitmaps of bigger size, this method must be called from a non-UI * thread. **/ public static Bitmap scaleDownBitmap(Context ctx, Bitmap source, int newHeight) { final float densityMultiplier = Utils.getDensityMultiplier(ctx); // Log.v( TAG, "#scaleDownBitmap Original w: " + source.getWidth() + " h: " + // source.getHeight() ); int h = (int) (newHeight * densityMultiplier); int w = (int) (h * source.getWidth() / ((double) source.getHeight())); // Log.v( TAG, "#scaleDownBitmap Computed w: " + w + " h: " + h ); Bitmap photo = Bitmap.createScaledBitmap(source, w, h, true); // Log.v( TAG, "#scaleDownBitmap Final w: " + w + " h: " + h ); return photo; } /** * Scales the image independently of the screen density of the device. Maintains image aspect * ratio. * <p> * When dealing with the bitmaps of bigger size, this method must be called from a non-UI * thread. */ public static Bitmap scaleBitmap(Context ctx, Bitmap source, int newHeight) { // Log.v( TAG, "#scaleDownBitmap Original w: " + source.getWidth() + " h: " + // source.getHeight() ); int w = (int) (newHeight * source.getWidth() / ((double) source.getHeight())); // Log.v( TAG, "#scaleDownBitmap Computed w: " + w + " h: " + newHeight ); Bitmap photo = Bitmap.createScaledBitmap(source, w, newHeight, true); // Log.v( TAG, "#scaleDownBitmap Final w: " + w + " h: " + newHeight ); return photo; } /** * Scales the image independently of the screen density of the device. Maintains image aspect * ratio. * * @param uri Uri of the source bitmap **/ public static Bitmap scaleDownBitmap(Context ctx, Uri uri, int newHeight) throws FileNotFoundException, IOException { Bitmap original = Media.getBitmap(ctx.getContentResolver(), uri); return scaleBitmap(ctx, original, newHeight); } /** * Scales the image independently of the screen density of the device. Maintains image aspect * ratio. * * @param uri Uri of the source bitmap **/ public static Uri scaleDownBitmapForUri(Context ctx, Uri uri, int newHeight) throws FileNotFoundException, IOException { if (uri == null) throw new NullPointerException(ERROR_URI_NULL); if (!MediaUtils.isMediaContentUri(uri)) return null; Bitmap original = Media.getBitmap(ctx.getContentResolver(), uri); Bitmap bmp = scaleBitmap(ctx, original, newHeight); Uri destUri = null; String uriStr = Utils.writeImageToMedia(ctx, bmp, "", ""); if (uriStr != null) { destUri = Uri.parse(uriStr); } return destUri; } /** * Gets the orientation of the image pointed to by the parameter uri * * @return Image orientation value corresponding to <code>ExifInterface.ORIENTATION_*</code> <br/> * Returns -1 if the row for the {@link android.net.Uri} is not found. **/ public static int getOrientation(Context context, Uri uri) { int invalidOrientation = -1; if (uri == null) { throw new NullPointerException(ERROR_URI_NULL); } if (!MediaUtils.isMediaContentUri(uri)) { return invalidOrientation; } String filePath = Utils.getPathForMediaUri(context, uri); ExifInterface exif = null; try { exif = new ExifInterface(filePath); } catch (IOException e) { e.printStackTrace(); } int orientation = invalidOrientation; if (exif != null) { orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, invalidOrientation); } return orientation; } /** * Rotate the image at the specified uri. For the rotation of the image the * {@link android.media.ExifInterface} data in the image will be used. * * @param uri Uri of the image to be rotated. **/ public static Uri rotateImage(Context context, Uri uri) throws FileNotFoundException, IOException { // rotate the image if (uri == null) { throw new NullPointerException(ERROR_URI_NULL); } if (!MediaUtils.isMediaContentUri(uri)) { return null; } int invalidOrientation = -1; byte[] data = Utils.getMediaData(context, uri); int orientation = getOrientation(context, uri); Uri newUri = null; try { Log.d(TAG, "#rotateImage Exif orientation: " + orientation); if (orientation != invalidOrientation) { Matrix matrix = new Matrix(); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: matrix.postRotate(90); break; case ExifInterface.ORIENTATION_ROTATE_180: matrix.postRotate(180); break; case ExifInterface.ORIENTATION_ROTATE_270: matrix.postRotate(270); break; } // set some options so the memory is manager properly BitmapFactory.Options options = new BitmapFactory.Options(); // options.inPreferredConfig = Bitmap.Config.RGB_565; // try to enable this if // OutOfMem issue still persists options.inPurgeable = true; options.inInputShareable = true; Bitmap original = BitmapFactory.decodeByteArray(data, 0, data.length, options); Bitmap rotatedBitmap = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true); // rotating // bitmap String newUrl = Media.insertImage(((Activity) context).getContentResolver(), rotatedBitmap, "", ""); if (newUrl != null) { newUri = Uri.parse(newUrl); } } } catch (Exception e) { e.printStackTrace(); } return newUri; } /** * Get the size of parameter {@link Bitmap}. This maybe a heavy operation. * Prefer not calling from main thread of the activity. * * @param data * @return */ public static int sizeOf(Bitmap data) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1) { return data.getRowBytes() * data.getHeight(); } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return data.getByteCount(); } else { return data.getAllocationByteCount(); } } }