Wednesday 28 June 2017

Image Utility


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

}

No comments:

Post a Comment