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