blob: 6f61f5608ab45eb5617c30eb2affd7b201e234f3 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mail.utils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
/**
* Provides static functions to decode bitmaps at the optimal size
*/
public class BitmapUtil {
private static final String TAG = LogTag.getLogTag();
private static final boolean DEBUG = false;
private BitmapUtil() {
}
/**
* Decode an image into a Bitmap, using sub-sampling if the hinted dimensions call for it.
* Does not crop to fit the hinted dimensions.
*
* @param src an encoded image
* @param w hint width in px
* @param h hint height in px
* @return a decoded Bitmap that is not exactly sized to the hinted dimensions.
*/
public static Bitmap decodeByteArray(byte[] src, int w, int h) {
try {
// calculate sample size based on w/h
final BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(src, 0, src.length, opts);
if (opts.mCancel || opts.outWidth == -1 || opts.outHeight == -1) {
return null;
}
opts.inSampleSize = Math.min(opts.outWidth / w, opts.outHeight / h);
opts.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(src, 0, src.length, opts);
} catch (Throwable t) {
LogUtils.w(TAG, t, "BitmapUtils unable to decode image");
return null;
}
}
/**
* Decode an image into a Bitmap, using sub-sampling if the desired dimensions call for it.
* Also applies a center-crop a la {@link android.widget.ImageView.ScaleType#CENTER_CROP}.
*
* @param src an encoded image
* @param w desired width in px
* @param h desired height in px
* @return an exactly-sized decoded Bitmap that is center-cropped.
*/
public static Bitmap decodeByteArrayWithCenterCrop(byte[] src, int w, int h) {
try {
final Bitmap decoded = decodeByteArray(src, w, h);
return centerCrop(decoded, w, h);
} catch (Throwable t) {
LogUtils.w(TAG, t, "BitmapUtils unable to crop image");
return null;
}
}
/**
* Returns a new Bitmap copy with a center-crop effect a la
* {@link android.widget.ImageView.ScaleType#CENTER_CROP}. May return the input bitmap if no
* scaling is necessary.
*
* @param src original bitmap of any size
* @param w desired width in px
* @param h desired height in px
* @return a copy of src conforming to the given width and height, or src itself if it already
* matches the given width and height
*/
public static Bitmap centerCrop(final Bitmap src, final int w, final int h) {
return crop(src, w, h, 0.5f, 0.5f);
}
/**
* Returns a new Bitmap copy with a crop effect depending on the crop anchor given. 0.5f is like
* {@link android.widget.ImageView.ScaleType#CENTER_CROP}. The crop anchor will be be nudged
* so the entire cropped bitmap will fit inside the src. May return the input bitmap if no
* scaling is necessary.
*
*
* Example of changing verticalCenterPercent:
* _________ _________
* | | | |
* | | |_________|
* | | | |/___0.3f
* |---------| |_________|\
* | |<---0.5f | |
* |---------| | |
* | | | |
* | | | |
* |_________| |_________|
*
* @param src original bitmap of any size
* @param w desired width in px
* @param h desired height in px
* @param horizontalCenterPercent determines which part of the src to crop from. Range from 0
* .0f to 1.0f. The value determines which part of the src
* maps to the horizontal center of the resulting bitmap.
* @param verticalCenterPercent determines which part of the src to crop from. Range from 0
* .0f to 1.0f. The value determines which part of the src maps
* to the vertical center of the resulting bitmap.
* @return a copy of src conforming to the given width and height, or src itself if it already
* matches the given width and height
*/
public static Bitmap crop(final Bitmap src, final int w, final int h,
final float horizontalCenterPercent, final float verticalCenterPercent) {
if (horizontalCenterPercent < 0 || horizontalCenterPercent > 1 || verticalCenterPercent < 0
|| verticalCenterPercent > 1) {
throw new IllegalArgumentException(
"horizontalCenterPercent and verticalCenterPercent must be between 0.0f and "
+ "1.0f, inclusive.");
}
final int srcWidth = src.getWidth();
final int srcHeight = src.getHeight();
// exit early if no resize/crop needed
if (w == srcWidth && h == srcHeight) {
return src;
}
final Matrix m = new Matrix();
final float scale = Math.max(
(float) w / srcWidth,
(float) h / srcHeight);
m.setScale(scale, scale);
final int srcCroppedW, srcCroppedH;
int srcX, srcY;
srcCroppedW = Math.round(w / scale);
srcCroppedH = Math.round(h / scale);
srcX = (int) (srcWidth * horizontalCenterPercent - srcCroppedW / 2);
srcY = (int) (srcHeight * verticalCenterPercent - srcCroppedH / 2);
// Nudge srcX and srcY to be within the bounds of src
srcX = Math.max(Math.min(srcX, srcWidth - srcCroppedW), 0);
srcY = Math.max(Math.min(srcY, srcHeight - srcCroppedH), 0);
final Bitmap cropped = Bitmap.createBitmap(src, srcX, srcY, srcCroppedW, srcCroppedH, m,
true /* filter */);
if (DEBUG) LogUtils.i(TAG,
"BitmapUtils IN centerCrop, srcW/H=%s/%s desiredW/H=%s/%s srcX/Y=%s/%s" +
" innerW/H=%s/%s scale=%s resultW/H=%s/%s",
srcWidth, srcHeight, w, h, srcX, srcY, srcCroppedW, srcCroppedH, scale,
cropped.getWidth(), cropped.getHeight());
if (DEBUG && (w != cropped.getWidth() || h != cropped.getHeight())) {
LogUtils.e(TAG, new Error(), "BitmapUtils last center crop violated assumptions.");
}
return cropped;
}
}