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