Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.mail.utils;
     18 import android.graphics.Bitmap;
     19 import android.graphics.BitmapFactory;
     20 import android.graphics.Matrix;
     21 
     22 /**
     23  * Provides static functions to decode bitmaps at the optimal size
     24  */
     25 public class BitmapUtil {
     26 
     27     private static final String TAG = LogTag.getLogTag();
     28     private static final boolean DEBUG = false;
     29 
     30     private BitmapUtil() {
     31     }
     32 
     33     /**
     34      * Decode an image into a Bitmap, using sub-sampling if the hinted dimensions call for it.
     35      * Does not crop to fit the hinted dimensions.
     36      *
     37      * @param src an encoded image
     38      * @param w hint width in px
     39      * @param h hint height in px
     40      * @return a decoded Bitmap that is not exactly sized to the hinted dimensions.
     41      */
     42     public static Bitmap decodeByteArray(byte[] src, int w, int h) {
     43         try {
     44             // calculate sample size based on w/h
     45             final BitmapFactory.Options opts = new BitmapFactory.Options();
     46             opts.inJustDecodeBounds = true;
     47             BitmapFactory.decodeByteArray(src, 0, src.length, opts);
     48             if (opts.mCancel || opts.outWidth == -1 || opts.outHeight == -1) {
     49                 return null;
     50             }
     51             opts.inSampleSize = Math.min(opts.outWidth / w, opts.outHeight / h);
     52             opts.inJustDecodeBounds = false;
     53             return BitmapFactory.decodeByteArray(src, 0, src.length, opts);
     54         } catch (Throwable t) {
     55             LogUtils.w(TAG, t, "BitmapUtils unable to decode image");
     56             return null;
     57         }
     58     }
     59 
     60     /**
     61      * Decode an image into a Bitmap, using sub-sampling if the desired dimensions call for it.
     62      * Also applies a center-crop a la {@link android.widget.ImageView.ScaleType#CENTER_CROP}.
     63      *
     64      * @param src an encoded image
     65      * @param w desired width in px
     66      * @param h desired height in px
     67      * @return an exactly-sized decoded Bitmap that is center-cropped.
     68      */
     69     public static Bitmap decodeByteArrayWithCenterCrop(byte[] src, int w, int h) {
     70         try {
     71             final Bitmap decoded = decodeByteArray(src, w, h);
     72             return centerCrop(decoded, w, h);
     73 
     74         } catch (Throwable t) {
     75             LogUtils.w(TAG, t, "BitmapUtils unable to crop image");
     76             return null;
     77         }
     78     }
     79 
     80     /**
     81      * Returns a new Bitmap copy with a center-crop effect a la
     82      * {@link android.widget.ImageView.ScaleType#CENTER_CROP}. May return the input bitmap if no
     83      * scaling is necessary.
     84      *
     85      * @param src original bitmap of any size
     86      * @param w desired width in px
     87      * @param h desired height in px
     88      * @return a copy of src conforming to the given width and height, or src itself if it already
     89      *         matches the given width and height
     90      */
     91     public static Bitmap centerCrop(final Bitmap src, final int w, final int h) {
     92         return crop(src, w, h, 0.5f, 0.5f);
     93     }
     94 
     95     /**
     96      * Returns a new Bitmap copy with a crop effect depending on the crop anchor given. 0.5f is like
     97      * {@link android.widget.ImageView.ScaleType#CENTER_CROP}. The crop anchor will be be nudged
     98      * so the entire cropped bitmap will fit inside the src. May return the input bitmap if no
     99      * scaling is necessary.
    100      *
    101      *
    102      * Example of changing verticalCenterPercent:
    103      *   _________            _________
    104      *  |         |          |         |
    105      *  |         |          |_________|
    106      *  |         |          |         |/___0.3f
    107      *  |---------|          |_________|\
    108      *  |         |<---0.5f  |         |
    109      *  |---------|          |         |
    110      *  |         |          |         |
    111      *  |         |          |         |
    112      *  |_________|          |_________|
    113      *
    114      * @param src original bitmap of any size
    115      * @param w desired width in px
    116      * @param h desired height in px
    117      * @param horizontalCenterPercent determines which part of the src to crop from. Range from 0
    118      *                                .0f to 1.0f. The value determines which part of the src
    119      *                                maps to the horizontal center of the resulting bitmap.
    120      * @param verticalCenterPercent determines which part of the src to crop from. Range from 0
    121      *                              .0f to 1.0f. The value determines which part of the src maps
    122      *                              to the vertical center of the resulting bitmap.
    123      * @return a copy of src conforming to the given width and height, or src itself if it already
    124      *         matches the given width and height
    125      */
    126     public static Bitmap crop(final Bitmap src, final int w, final int h,
    127             final float horizontalCenterPercent, final float verticalCenterPercent) {
    128         if (horizontalCenterPercent < 0 || horizontalCenterPercent > 1 || verticalCenterPercent < 0
    129                 || verticalCenterPercent > 1) {
    130             throw new IllegalArgumentException(
    131                     "horizontalCenterPercent and verticalCenterPercent must be between 0.0f and "
    132                             + "1.0f, inclusive.");
    133         }
    134         final int srcWidth = src.getWidth();
    135         final int srcHeight = src.getHeight();
    136 
    137         // exit early if no resize/crop needed
    138         if (w == srcWidth && h == srcHeight) {
    139             return src;
    140         }
    141 
    142         final Matrix m = new Matrix();
    143         final float scale = Math.max(
    144                 (float) w / srcWidth,
    145                 (float) h / srcHeight);
    146         m.setScale(scale, scale);
    147 
    148         final int srcCroppedW, srcCroppedH;
    149         int srcX, srcY;
    150 
    151         srcCroppedW = Math.round(w / scale);
    152         srcCroppedH = Math.round(h / scale);
    153         srcX = (int) (srcWidth * horizontalCenterPercent - srcCroppedW / 2);
    154         srcY = (int) (srcHeight * verticalCenterPercent - srcCroppedH / 2);
    155 
    156         // Nudge srcX and srcY to be within the bounds of src
    157         srcX = Math.max(Math.min(srcX, srcWidth - srcCroppedW), 0);
    158         srcY = Math.max(Math.min(srcY, srcHeight - srcCroppedH), 0);
    159 
    160         final Bitmap cropped = Bitmap.createBitmap(src, srcX, srcY, srcCroppedW, srcCroppedH, m,
    161                 true /* filter */);
    162 
    163         if (DEBUG) LogUtils.i(TAG,
    164                 "BitmapUtils IN centerCrop, srcW/H=%s/%s desiredW/H=%s/%s srcX/Y=%s/%s" +
    165                 " innerW/H=%s/%s scale=%s resultW/H=%s/%s",
    166                 srcWidth, srcHeight, w, h, srcX, srcY, srcCroppedW, srcCroppedH, scale,
    167                 cropped.getWidth(), cropped.getHeight());
    168         if (DEBUG && (w != cropped.getWidth() || h != cropped.getHeight())) {
    169             LogUtils.e(TAG, new Error(), "BitmapUtils last center crop violated assumptions.");
    170         }
    171 
    172         return cropped;
    173     }
    174 
    175 }
    176