1. Crete local bitmap from uri
2. Decode input stream of image and get bitmap
3. Gets the image bounds
4. Resize bitmap
5. Rotate image
6. Get Image orientation
/**
* Image utilities
*/
public class ImageUtils {
// Logging
private static final String TAG = "ImageUtils";
/** Minimum class memory class to use full-res photos */
private final static long MIN_NORMAL_CLASS = 32;
/** Minimum class memory class to use small photos */
private final static long MIN_SMALL_CLASS = 24;
private static final String BASE64_URI_PREFIX = "base64,";
private static final Pattern BASE64_IMAGE_URI_PATTERN = Pattern.compile("^(?:.*;)?base64,.*");
public static enum ImageSize {
EXTRA_SMALL,
SMALL,
NORMAL,
}
public static final ImageSize sUseImageSize;
static {
// On HC and beyond, assume devices are more capable
if (Build.VERSION.SDK_INT >= 11) {
sUseImageSize = ImageSize.NORMAL;
} else {
if (PhotoViewActivity.sMemoryClass >= MIN_NORMAL_CLASS) {
// We have plenty of memory; use full sized photos
sUseImageSize = ImageSize.NORMAL;
} else if (PhotoViewActivity.sMemoryClass >= MIN_SMALL_CLASS) {
// We have slight less memory; use smaller sized photos
sUseImageSize = ImageSize.SMALL;
} else {
// We have little memory; use very small sized photos
sUseImageSize = ImageSize.EXTRA_SMALL;
}
}
}
/**
* @return true if the MimeType type is image
*/
public static boolean isImageMimeType(String mimeType) {
return mimeType != null && mimeType.startsWith("image/");
}
/**
* 01. Create a bitmap from a local URI
*
* @param resolver The ContentResolver
* @param uri The local URI
* @param maxSize The maximum size (either width or height)
*
* @return The new bitmap or null
*/
public static Bitmap createLocalBitmap(ContentResolver resolver, Uri uri, int maxSize) {
// TODO: make this method not download the image for both getImageBounds and decodeStream
InputStream inputStream = null;
try {
final BitmapFactory.Options opts = new BitmapFactory.Options();
final Point bounds = getImageBounds(resolver, uri);
inputStream = openInputStream(resolver, uri);
if (bounds == null || inputStream == null) {
return null;
}
opts.inSampleSize = Math.max(bounds.x / maxSize, bounds.y / maxSize);
final Bitmap decodedBitmap = decodeStream(inputStream, null, opts);
// Correct thumbnail orientation as necessary
// TODO: Fix rotation if it's actually a problem
//return rotateBitmap(resolver, uri, decodedBitmap);
return decodedBitmap;
} catch (FileNotFoundException exception) {
// Do nothing - the photo will appear to be missing
} catch (IOException exception) {
// Do nothing - the photo will appear to be missing
} catch (IllegalArgumentException exception) {
// Do nothing - the photo will appear to be missing
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException ignore) {
}
}
return null;
}
/**
* 02. Decode inputstream of image and get bitmap
* Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect,
* BitmapFactory.Options)} that returns {@code null} on {@link
* OutOfMemoryError}.
*
* @param is The input stream that holds the raw data to be decoded into a
* bitmap.
* @param outPadding If not null, return the padding rect for the bitmap if
* it exists, otherwise set padding to [-1,-1,-1,-1]. If
* no bitmap is returned (null) then padding is
* unchanged.
* @param opts null-ok; Options that control downsampling and whether the
* image should be completely decoded, or just is size returned.
* @return The decoded bitmap, or null if the image data could not be
* decoded, or, if opts is non-null, if opts requested only the
* size be returned (in opts.outWidth and opts.outHeight)
*/
public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) {
ByteArrayOutputStream out = null;
InputStream byteStream = null;
try {
out = new ByteArrayOutputStream();
final byte[] buffer = new byte[4096];
int n = is.read(buffer);
while (n >= 0) {
out.write(buffer, 0, n);
n = is.read(buffer);
}
final byte[] bitmapBytes = out.toByteArray();
// Determine the orientation for this image
final int orientation = Exif.getOrientation(bitmapBytes);
// Create an InputStream from this byte array
byteStream = new ByteArrayInputStream(bitmapBytes);
final Bitmap originalBitmap = BitmapFactory.decodeStream(byteStream, outPadding, opts);
if (originalBitmap != null && orientation != 0) {
final Matrix matrix = new Matrix();
matrix.postRotate(orientation);
return Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(),
originalBitmap.getHeight(), matrix, true);
}
return originalBitmap;
} catch (OutOfMemoryError oome) {
Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome);
return null;
} catch (IOException ioe) {
Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an IOE", ioe);
return null;
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
// Do nothing
}
}
if (byteStream != null) {
try {
byteStream.close();
} catch (IOException e) {
// Do nothing
}
}
}
}
/**
* 03. Gets the image bounds
*
* @param resolver The ContentResolver
* @param uri The uri
*
* @return The image bounds
*/
private static Point getImageBounds(ContentResolver resolver, Uri uri)
throws IOException {
final BitmapFactory.Options opts = new BitmapFactory.Options();
InputStream inputStream = null;
String scheme = uri.getScheme();
try {
opts.inJustDecodeBounds = true;
inputStream = openInputStream(resolver, uri);
if (inputStream == null) {
return null;
}
decodeStream(inputStream, null, opts);
return new Point(opts.outWidth, opts.outHeight);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException ignore) {
}
}
}
private static InputStream openInputStream(ContentResolver resolver, Uri uri) throws
FileNotFoundException {
String scheme = uri.getScheme();
if ("http".equals(scheme) || "https".equals(scheme)) {
try {
return new URL(uri.toString()).openStream();
} catch (MalformedURLException e) {
// Fall-back to the previous behaviour, just in case
Log.w(TAG, "Could not convert the uri to url: " + uri.toString());
return resolver.openInputStream(uri);
} catch (IOException e) {
Log.w(TAG, "Could not open input stream for uri: " + uri.toString());
return null;
}
} else if ("data".equals(scheme)) {
byte[] data = parseDataUri(uri);
if (data != null) {
return new ByteArrayInputStream(data);
}
}
return resolver.openInputStream(uri);
}
private static byte[] parseDataUri(Uri uri) {
String ssp = uri.getSchemeSpecificPart();
try {
if (ssp.startsWith(BASE64_URI_PREFIX)) {
String base64 = ssp.substring(BASE64_URI_PREFIX.length());
return Base64.decode(base64, Base64.URL_SAFE);
} else if (BASE64_IMAGE_URI_PATTERN.matcher(ssp).matches()){
String base64 = ssp.substring(
ssp.indexOf(BASE64_URI_PREFIX) + BASE64_URI_PREFIX.length());
return Base64.decode(base64, Base64.DEFAULT);
} else {
return null;
}
} catch (IllegalArgumentException ex) {
Log.e(TAG, "Mailformed data URI: " + ex);
return null;
}
}
}
========================
4. Resize bitmap
public static Bitmap resizeBitmap(Bitmap org, int destWidth, int destHeight) {
Bitmap blankResized = Bitmap.createBitmap(destWidth, destHeight, Bitmap.Config.ARGB_8888);
int xPadding = 0;
int yPadding = 0;
int scaledWidth = blankResized.getWidth();
int scaledHeight = blankResized.getHeight();
float scale = 1;
if (org.getWidth() > org.getHeight()) {
scale = (float) blankResized.getWidth() / (float) org.getWidth();
scaledHeight = (int) (org.getHeight() * scale);
yPadding = (blankResized.getHeight() - scaledHeight) / 2;
}
else {
scale = (float) blankResized.getHeight() / (float) org.getHeight();
scaledWidth = (int) (org.getWidth() * scale);
xPadding = (blankResized.getWidth() - scaledWidth) / 2;
}
Bitmap orgScale = Bitmap.createScaledBitmap(org, scaledWidth, scaledHeight, true);
Canvas canvas = new Canvas(blankResized);
canvas.drawBitmap(
orgScale,
new Rect(0, 0, scaledWidth, scaledHeight),
new Rect(xPadding, yPadding, xPadding + scaledWidth, yPadding + scaledHeight),
null);
return blankResized;
}
====================================
5. Rotate image
public static Bitmap rotateImage(final Bitmap bitmap, final File fileWithExifInfo) {
Bitmap rotatedBitmap = bitmap;
int orientation = 0;
try {
orientation = getImageOrientation(fileWithExifInfo.getAbsolutePath());
if (orientation != 0) {
final Matrix matrix = new Matrix();
matrix.postRotate(orientation);
rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
bitmap.recycle();
}
} catch (final IOException e) {
}
return rotatedBitmap;
}
==================================
6. Get image orientation
public static int getImageOrientation(final String file) throws IOException {
final ExifInterface exif = new ExifInterface(file);
final int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
return 0;
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
return 0;
}
}