Home | History | Annotate | Download | only in contacts
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License
     15  */
     16 package com.android.providers.contacts;
     17 
     18 import android.graphics.Bitmap;
     19 import android.graphics.BitmapFactory;
     20 import android.graphics.Matrix;
     21 
     22 import java.io.ByteArrayOutputStream;
     23 import java.io.IOException;
     24 
     25 /**
     26  * Class that converts a bitmap (or byte array representing a bitmap) into a display
     27  * photo and a thumbnail photo.
     28  */
     29 /* package-protected */ final class PhotoProcessor {
     30 
     31     private final int mMaxDisplayPhotoDim;
     32     private final int mMaxThumbnailPhotoDim;
     33     private final boolean mForceCropToSquare;
     34     private final Bitmap mOriginal;
     35     private Bitmap mDisplayPhoto;
     36     private Bitmap mThumbnailPhoto;
     37 
     38     /**
     39      * Initializes a photo processor for the given bitmap.
     40      * @param original The bitmap to process.
     41      * @param maxDisplayPhotoDim The maximum height and width for the display photo.
     42      * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo.
     43      * @throws IOException If bitmap decoding or scaling fails.
     44      */
     45     public PhotoProcessor(Bitmap original, int maxDisplayPhotoDim, int maxThumbnailPhotoDim)
     46             throws IOException {
     47         this(original, maxDisplayPhotoDim, maxThumbnailPhotoDim, false);
     48     }
     49 
     50     /**
     51      * Initializes a photo processor for the given bitmap.
     52      * @param originalBytes A byte array to decode into a bitmap to process.
     53      * @param maxDisplayPhotoDim The maximum height and width for the display photo.
     54      * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo.
     55      * @throws IOException If bitmap decoding or scaling fails.
     56      */
     57     public PhotoProcessor(byte[] originalBytes, int maxDisplayPhotoDim, int maxThumbnailPhotoDim)
     58             throws IOException {
     59         this(BitmapFactory.decodeByteArray(originalBytes, 0, originalBytes.length),
     60                 maxDisplayPhotoDim, maxThumbnailPhotoDim, false);
     61     }
     62 
     63     /**
     64      * Initializes a photo processor for the given bitmap.
     65      * @param original The bitmap to process.
     66      * @param maxDisplayPhotoDim The maximum height and width for the display photo.
     67      * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo.
     68      * @param forceCropToSquare Whether to force the processed images to be square.  If the source
     69      *     photo is not square, this will crop to the square at the center of the image's rectangle.
     70      *     If this is not set to true, the image will simply be downscaled to fit in the given
     71      *     dimensions, retaining its original aspect ratio.
     72      * @throws IOException If bitmap decoding or scaling fails.
     73      */
     74     public PhotoProcessor(Bitmap original, int maxDisplayPhotoDim, int maxThumbnailPhotoDim,
     75             boolean forceCropToSquare) throws IOException {
     76         mOriginal = original;
     77         mMaxDisplayPhotoDim = maxDisplayPhotoDim;
     78         mMaxThumbnailPhotoDim = maxThumbnailPhotoDim;
     79         mForceCropToSquare = forceCropToSquare;
     80         process();
     81     }
     82 
     83     /**
     84      * Initializes a photo processor for the given bitmap.
     85      * @param originalBytes A byte array to decode into a bitmap to process.
     86      * @param maxDisplayPhotoDim The maximum height and width for the display photo.
     87      * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo.
     88      * @param forceCropToSquare Whether to force the processed images to be square.  If the source
     89      *     photo is not square, this will crop to the square at the center of the image's rectangle.
     90      *     If this is not set to true, the image will simply be downscaled to fit in the given
     91      *     dimensions, retaining its original aspect ratio.
     92      * @throws IOException If bitmap decoding or scaling fails.
     93      */
     94     public PhotoProcessor(byte[] originalBytes, int maxDisplayPhotoDim, int maxThumbnailPhotoDim,
     95             boolean forceCropToSquare) throws IOException {
     96         this(BitmapFactory.decodeByteArray(originalBytes, 0, originalBytes.length),
     97                 maxDisplayPhotoDim, maxThumbnailPhotoDim, forceCropToSquare);
     98     }
     99 
    100     /**
    101      * Processes the original image, producing a scaled-down display photo and thumbnail photo.
    102      * @throws IOException If bitmap decoding or scaling fails.
    103      */
    104     private void process() throws IOException {
    105         if (mOriginal == null) {
    106             throw new IOException("Invalid image file");
    107         }
    108         mDisplayPhoto = getScaledBitmap(mMaxDisplayPhotoDim);
    109         mThumbnailPhoto = getScaledBitmap(mMaxThumbnailPhotoDim);
    110     }
    111 
    112     /**
    113      * Scales down the original bitmap to fit within the given maximum width and height.
    114      * If the bitmap already fits in those dimensions, the original bitmap will be
    115      * returned unmodified unless the photo processor is set up to crop it to a square.
    116      * @param maxDim Maximum width and height (in pixels) for the image.
    117      * @return A bitmap that fits the maximum dimensions.
    118      */
    119     @SuppressWarnings({"SuspiciousNameCombination"})
    120     private Bitmap getScaledBitmap(int maxDim) {
    121         Bitmap scaledBitmap = mOriginal;
    122         int width = mOriginal.getWidth();
    123         int height = mOriginal.getHeight();
    124         int cropLeft = 0;
    125         int cropTop = 0;
    126         if (mForceCropToSquare && width != height) {
    127             // Crop the image to the square at its center.
    128             if (height > width) {
    129                 cropTop = (height - width) / 2;
    130                 height = width;
    131             } else {
    132                 cropLeft = (width - height) / 2;
    133                 width = height;
    134             }
    135         }
    136         float scaleFactor = ((float) maxDim) / Math.max(width, height);
    137         if (scaleFactor < 1.0 || cropLeft != 0 || cropTop != 0) {
    138             // Need to scale or crop the photo.
    139             Matrix matrix = new Matrix();
    140             matrix.setScale(scaleFactor, scaleFactor);
    141             scaledBitmap = Bitmap.createBitmap(
    142                     mOriginal, cropLeft, cropTop, width, height, matrix, false);
    143         }
    144         return scaledBitmap;
    145     }
    146 
    147     /**
    148      * Helper method to compress the given bitmap as a JPEG and return the resulting byte array.
    149      */
    150     private byte[] getCompressedBytes(Bitmap b) throws IOException {
    151         ByteArrayOutputStream baos = new ByteArrayOutputStream();
    152         boolean compressed = b.compress(Bitmap.CompressFormat.JPEG, 95, baos);
    153         if (!compressed) {
    154             throw new IOException("Unable to compress image");
    155         }
    156         baos.flush();
    157         baos.close();
    158         return baos.toByteArray();
    159     }
    160 
    161     /**
    162      * Retrieves the uncompressed display photo.
    163      */
    164     public Bitmap getDisplayPhoto() {
    165         return mDisplayPhoto;
    166     }
    167 
    168     /**
    169      * Retrieves the uncompressed thumbnail photo.
    170      */
    171     public Bitmap getThumbnailPhoto() {
    172         return mThumbnailPhoto;
    173     }
    174 
    175     /**
    176      * Retrieves the compressed display photo as a byte array.
    177      */
    178     public byte[] getDisplayPhotoBytes() throws IOException {
    179         return getCompressedBytes(mDisplayPhoto);
    180     }
    181 
    182     /**
    183      * Retrieves the compressed thumbnail photo as a byte array.
    184      */
    185     public byte[] getThumbnailPhotoBytes() throws IOException {
    186         return getCompressedBytes(mThumbnailPhoto);
    187     }
    188 
    189     /**
    190      * Retrieves the maximum width or height (in pixels) of the display photo.
    191      */
    192     public int getMaxDisplayPhotoDim() {
    193         return mMaxDisplayPhotoDim;
    194     }
    195 
    196     /**
    197      * Retrieves the maximum width or height (in pixels) of the thumbnail.
    198      */
    199     public int getMaxThumbnailPhotoDim() {
    200         return mMaxThumbnailPhotoDim;
    201     }
    202 }
    203