package it.sephiroth.android.library.imagezoom.test.utils;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
public class DecodeUtils {
/**
* Try to load a {@link Bitmap} from the passed {@link Uri} ( a file, a content or an url )
*
* @param context the current app context
* @param uri the image source
* @param maxW the final image maximum width
* @param maxH the final image maximum height
* @return the loaded and resized bitmap, if success, or null if load was unsuccesful
*/
public static Bitmap decode( Context context, Uri uri, int maxW, int maxH ) {
InputStream stream = openInputStream( context, uri );
if ( null == stream ) {
return null;
}
int orientation = ExifUtils.getExifOrientation( context, uri );
Bitmap bitmap = null;
int[] imageSize = new int[2];
final boolean decoded = decodeImageBounds( stream, imageSize );
IOUtils.closeSilently( stream );
if ( decoded ) {
int sampleSize;
if( maxW < 0 || maxH < 0 ) {
sampleSize = 1;
} else {
sampleSize = computeSampleSize( imageSize[0], imageSize[1], (int) ( maxW * 1.2 ), (int) ( maxH * 1.2 ), orientation );
}
BitmapFactory.Options options = getDefaultOptions();
options.inSampleSize = sampleSize;
bitmap = decodeBitmap( context, uri, options, maxW, maxH, orientation, 0 );
}
return bitmap;
}
static Bitmap decodeBitmap( Context context, Uri uri, BitmapFactory.Options options, int maxW, int maxH,
int orientation, int pass ) {
Bitmap bitmap = null;
Bitmap newBitmap = null;
if ( pass > 20 ) {
return null;
}
InputStream stream = openInputStream( context, uri );
if( null == stream ) return null;
try {
// decode the bitmap via android BitmapFactory
bitmap = BitmapFactory.decodeStream( stream, null, options );
IOUtils.closeSilently( stream );
if ( bitmap != null ) {
if( maxW > 0 && maxH > 0 ) {
newBitmap = BitmapUtils.resizeBitmap( bitmap, maxW, maxH, orientation );
if ( bitmap != newBitmap ) {
bitmap.recycle();
}
bitmap = newBitmap;
}
}
} catch ( OutOfMemoryError error ) {
IOUtils.closeSilently( stream );
if ( null != bitmap ) {
bitmap.recycle();
}
options.inSampleSize += 1;
bitmap = decodeBitmap( context, uri, options, maxW, maxH, orientation, pass + 1 );
}
return bitmap;
}
/**
* Return an {@link InputStream} from the given uri. ( can be a local content, a file path or an http url )
*
* @param context
* @param uri
* @return the {@link InputStream} from the given uri, null if uri cannot be opened
*/
public static InputStream openInputStream( Context context, Uri uri ) {
if ( null == uri ) return null;
final String scheme = uri.getScheme();
InputStream stream = null;
if ( scheme == null || ContentResolver.SCHEME_FILE.equals( scheme ) ) {
// from file
stream = openFileInputStream( uri.getPath() );
} else if ( ContentResolver.SCHEME_CONTENT.equals( scheme ) ) {
// from content
stream = openContentInputStream( context, uri );
} else if ( "http".equals( scheme ) || "https".equals( scheme ) ) {
// from remote uri
stream = openRemoteInputStream( uri );
}
return stream;
}
public static boolean decodeImageBounds( final InputStream stream, int[] outSize ) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream( stream, null, options );
if ( options.outHeight > 0 && options.outWidth > 0 ) {
outSize[0] = options.outWidth;
outSize[1] = options.outHeight;
return true;
}
return false;
}
private static int computeSampleSize( final int bitmapW, final int bitmapH, final int maxW, final int maxH,
final int orientation ) {
double w, h;
if ( orientation == 0 || orientation == 180 ) {
w = bitmapW;
h = bitmapH;
} else {
w = bitmapH;
h = bitmapW;
}
final int sampleSize = (int) Math.ceil( Math.max( w / maxW, h / maxH ) );
return sampleSize;
}
/**
* Return a {@link FileInputStream} from the given path or null if file not found
*
* @param path
* the file path
* @return the {@link FileInputStream} of the given path, null if {@link FileNotFoundException} is thrown
*/
static InputStream openFileInputStream( String path ) {
try {
return new FileInputStream( path );
} catch ( FileNotFoundException e ) {
e.printStackTrace();
}
return null;
}
/**
* Return a {@link BufferedInputStream} from the given uri or null if an exception is thrown
*
* @param context
* @param uri
* @return the {@link InputStream} of the given path. null if file is not found
*/
static InputStream openContentInputStream( Context context, Uri uri ) {
try {
return context.getContentResolver().openInputStream( uri );
} catch ( FileNotFoundException e ) {
e.printStackTrace();
}
return null;
}
/**
* Return an {@link InputStream} from the given url or null if failed to retrieve the content
*
* @param uri
* @return
*/
static InputStream openRemoteInputStream( Uri uri ) {
java.net.URL finalUrl;
try {
finalUrl = new java.net.URL( uri.toString() );
} catch ( MalformedURLException e ) {
e.printStackTrace();
return null;
}
HttpURLConnection connection;
try {
connection = (HttpURLConnection) finalUrl.openConnection();
} catch ( IOException e ) {
e.printStackTrace();
return null;
}
connection.setInstanceFollowRedirects( false );
int code;
try {
code = connection.getResponseCode();
} catch ( IOException e ) {
e.printStackTrace();
return null;
}
// permanent redirection
if ( code == HttpURLConnection.HTTP_MOVED_PERM || code == HttpURLConnection.HTTP_MOVED_TEMP
|| code == HttpURLConnection.HTTP_SEE_OTHER ) {
String newLocation = connection.getHeaderField( "Location" );
return openRemoteInputStream( Uri.parse( newLocation ) );
}
try {
return (InputStream) finalUrl.getContent();
} catch ( IOException e ) {
e.printStackTrace();
return null;
}
}
static BitmapFactory.Options getDefaultOptions() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inDither = false;
options.inJustDecodeBounds = false;
options.inPurgeable = true;
options.inInputShareable = true;
options.inTempStorage = new byte[16 * 1024];
return options;
}
}