• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BitmapUtil()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      */
decodeByteArray(byte[] src, int w, int h)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      */
decodeByteArrayWithCenterCrop(byte[] src, int w, int h)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      */
centerCrop(final Bitmap src, final int w, final int h)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      */
crop(final Bitmap src, final int w, final int h, final float horizontalCenterPercent, final float verticalCenterPercent)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