15211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook/* 25211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * Copyright (C) 2011 Google Inc. 35211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * Licensed to The Android Open Source Project. 45211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * 55211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * Licensed under the Apache License, Version 2.0 (the "License"); 65211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * you may not use this file except in compliance with the License. 75211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * You may obtain a copy of the License at 85211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * 95211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * http://www.apache.org/licenses/LICENSE-2.0 105211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * 115211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * Unless required by applicable law or agreed to in writing, software 125211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * distributed under the License is distributed on an "AS IS" BASIS, 135211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 145211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * See the License for the specific language governing permissions and 155211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * limitations under the License. 165211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook */ 175211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 185211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookpackage com.android.ex.photo.util; 195211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 205211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.content.ContentResolver; 215211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.graphics.Bitmap; 225211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.graphics.BitmapFactory; 235211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.graphics.Matrix; 245211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.graphics.Point; 255211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.graphics.Rect; 265211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.net.Uri; 275211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.os.Build; 285211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.util.DisplayMetrics; 295211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport android.util.Log; 305211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 315211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport com.android.ex.photo.PhotoViewActivity; 325211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport com.android.ex.photo.util.Exif; 335211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 345211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport java.io.ByteArrayOutputStream; 355211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport java.io.FileNotFoundException; 365211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport java.io.IOException; 375211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookimport java.io.InputStream; 385211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 395211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook/** 405211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * Image utilities 415211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook */ 425211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrookpublic class ImageUtils { 435211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // Logging 445211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook private static final String TAG = "ImageUtils"; 455211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 465211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook /** Minimum class memory class to use full-res photos */ 475211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook private final static long MIN_NORMAL_CLASS = 32; 485211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook /** Minimum class memory class to use small photos */ 495211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook private final static long MIN_SMALL_CLASS = 24; 505211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 515211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook public static enum ImageSize { 525211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook EXTRA_SMALL, 535211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook SMALL, 545211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook NORMAL, 555211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 565211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 575211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook public static final ImageSize sUseImageSize; 585211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook static { 595211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // On HC and beyond, assume devices are more capable 605211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook if (Build.VERSION.SDK_INT >= 11) { 615211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook sUseImageSize = ImageSize.NORMAL; 625211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } else { 635211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook if (PhotoViewActivity.sMemoryClass >= MIN_NORMAL_CLASS) { 645211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // We have plenty of memory; use full sized photos 655211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook sUseImageSize = ImageSize.NORMAL; 665211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } else if (PhotoViewActivity.sMemoryClass >= MIN_SMALL_CLASS) { 675211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // We have slight less memory; use smaller sized photos 685211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook sUseImageSize = ImageSize.SMALL; 695211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } else { 705211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // We have little memory; use very small sized photos 715211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook sUseImageSize = ImageSize.EXTRA_SMALL; 725211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 735211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 745211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 755211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 765211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook /** 775211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @return true if the MimeType type is image 785211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook */ 795211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook public static boolean isImageMimeType(String mimeType) { 805211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook return mimeType != null && mimeType.startsWith("image/"); 815211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 825211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 835211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook /** 845211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * Create a bitmap from a local URI 855211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * 865211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @param resolver The ContentResolver 875211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @param uri The local URI 885211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @param maxSize The maximum size (either width or height) 895211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * 905211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @return The new bitmap or null 915211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook */ 925211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook public static Bitmap createLocalBitmap(ContentResolver resolver, Uri uri, int maxSize) { 935211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook InputStream inputStream = null; 945211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook try { 955211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook final BitmapFactory.Options opts = new BitmapFactory.Options(); 965211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook final Point bounds = getImageBounds(resolver, uri); 975211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 985211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook inputStream = resolver.openInputStream(uri); 995211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook opts.inSampleSize = Math.max(bounds.x / maxSize, bounds.y / maxSize); 1005211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 1015211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook final Bitmap decodedBitmap = decodeStream(inputStream, null, opts); 1025211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 1035211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // Correct thumbnail orientation as necessary 1041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // TODO: Fix rotation if it's actually a problem 1051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein //return rotateBitmap(resolver, uri, decodedBitmap); 1061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return decodedBitmap; 1075211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 1085211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } catch (FileNotFoundException exception) { 1095211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // Do nothing - the photo will appear to be missing 1105211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } catch (IOException exception) { 1115211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // Do nothing - the photo will appear to be missing 1128085e1fcda882074ed387d3e999c448a84d6eb3bAndrew Sapperstein } catch (IllegalArgumentException exception) { 1138085e1fcda882074ed387d3e999c448a84d6eb3bAndrew Sapperstein // Do nothing - the photo will appear to be missing 1145211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } finally { 1155211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook try { 1165211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook if (inputStream != null) { 1175211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook inputStream.close(); 1185211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1195211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } catch (IOException ignore) { 1205211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1215211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1225211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook return null; 1235211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1245211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 1255211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook /** 1265211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect, 1275211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * BitmapFactory.Options)} that returns {@code null} on {@link 1285211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * OutOfMemoryError}. 1295211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * 1305211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @param is The input stream that holds the raw data to be decoded into a 1315211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * bitmap. 1325211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @param outPadding If not null, return the padding rect for the bitmap if 1335211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * it exists, otherwise set padding to [-1,-1,-1,-1]. If 1345211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * no bitmap is returned (null) then padding is 1355211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * unchanged. 1365211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @param opts null-ok; Options that control downsampling and whether the 1375211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * image should be completely decoded, or just is size returned. 1385211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @return The decoded bitmap, or null if the image data could not be 1395211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * decoded, or, if opts is non-null, if opts requested only the 1405211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * size be returned (in opts.outWidth and opts.outHeight) 1415211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook */ 1425211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) { 1435211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook ByteArrayOutputStream out = null; 1445211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook try { 1455211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook out = new ByteArrayOutputStream(); 1465211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook final byte[] buffer = new byte[4096]; 1475211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook int n = is.read(buffer); 1485211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook while (n >= 0) { 1495211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook out.write(buffer, 0, n); 1505211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook n = is.read(buffer); 1515211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1525211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook final byte[] bitmapBytes = out.toByteArray(); 1535211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 1545211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // Determine the orientation for this image 1555211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook final int orientation = Exif.getOrientation(bitmapBytes); 1565211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook final Bitmap originalBitmap = 1575211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length, opts); 1585211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 1595211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook if (originalBitmap != null && orientation != 0) { 1605211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook final Matrix matrix = new Matrix(); 1615211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook matrix.postRotate(orientation); 1625211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook return Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(), 1635211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook originalBitmap.getHeight(), matrix, true); 1645211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1655211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook return originalBitmap; 1665211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } catch (OutOfMemoryError oome) { 1675211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome); 1685211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook return null; 1695211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } catch (IOException ioe) { 1705211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an IOE", ioe); 1715211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook return null; 1725211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } finally { 1735211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook if (out != null) { 1745211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook try { 1755211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook out.close(); 1765211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } catch (IOException e) { 1775211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook // Do nothing 1785211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1795211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1805211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1815211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 1825211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 1835211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook /** 1845211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * Gets the image bounds 1855211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * 1865211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @param resolver The ContentResolver 1875211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @param uri The uri 1885211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * 1895211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook * @return The image bounds 1905211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook */ 1915211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook private static Point getImageBounds(ContentResolver resolver, Uri uri) 1925211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook throws IOException { 1935211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook final BitmapFactory.Options opts = new BitmapFactory.Options(); 1945211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook InputStream inputStream = null; 1955211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 1965211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook try { 1975211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook opts.inJustDecodeBounds = true; 1985211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook inputStream = resolver.openInputStream(uri); 1995211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook decodeStream(inputStream, null, opts); 2005211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook 2015211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook return new Point(opts.outWidth, opts.outHeight); 2025211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } finally { 2035211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook try { 2045211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook if (inputStream != null) { 2055211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook inputStream.close(); 2065211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 2075211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } catch (IOException ignore) { 2085211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 2095211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 2105211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook } 2115211e4682a26be787e60b1c56f56b113a2fac26cPaul Westbrook} 212