/* * Copyright (C) 2011 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.providers.contacts; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import java.io.ByteArrayOutputStream; import java.io.IOException; /** * Class that converts a bitmap (or byte array representing a bitmap) into a display * photo and a thumbnail photo. */ /* package-protected */ final class PhotoProcessor { private final int mMaxDisplayPhotoDim; private final int mMaxThumbnailPhotoDim; private final boolean mForceCropToSquare; private final Bitmap mOriginal; private Bitmap mDisplayPhoto; private Bitmap mThumbnailPhoto; /** * Initializes a photo processor for the given bitmap. * @param original The bitmap to process. * @param maxDisplayPhotoDim The maximum height and width for the display photo. * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo. * @throws IOException If bitmap decoding or scaling fails. */ public PhotoProcessor(Bitmap original, int maxDisplayPhotoDim, int maxThumbnailPhotoDim) throws IOException { this(original, maxDisplayPhotoDim, maxThumbnailPhotoDim, false); } /** * Initializes a photo processor for the given bitmap. * @param originalBytes A byte array to decode into a bitmap to process. * @param maxDisplayPhotoDim The maximum height and width for the display photo. * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo. * @throws IOException If bitmap decoding or scaling fails. */ public PhotoProcessor(byte[] originalBytes, int maxDisplayPhotoDim, int maxThumbnailPhotoDim) throws IOException { this(BitmapFactory.decodeByteArray(originalBytes, 0, originalBytes.length), maxDisplayPhotoDim, maxThumbnailPhotoDim, false); } /** * Initializes a photo processor for the given bitmap. * @param original The bitmap to process. * @param maxDisplayPhotoDim The maximum height and width for the display photo. * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo. * @param forceCropToSquare Whether to force the processed images to be square. If the source * photo is not square, this will crop to the square at the center of the image's rectangle. * If this is not set to true, the image will simply be downscaled to fit in the given * dimensions, retaining its original aspect ratio. * @throws IOException If bitmap decoding or scaling fails. */ public PhotoProcessor(Bitmap original, int maxDisplayPhotoDim, int maxThumbnailPhotoDim, boolean forceCropToSquare) throws IOException { mOriginal = original; mMaxDisplayPhotoDim = maxDisplayPhotoDim; mMaxThumbnailPhotoDim = maxThumbnailPhotoDim; mForceCropToSquare = forceCropToSquare; process(); } /** * Initializes a photo processor for the given bitmap. * @param originalBytes A byte array to decode into a bitmap to process. * @param maxDisplayPhotoDim The maximum height and width for the display photo. * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo. * @param forceCropToSquare Whether to force the processed images to be square. If the source * photo is not square, this will crop to the square at the center of the image's rectangle. * If this is not set to true, the image will simply be downscaled to fit in the given * dimensions, retaining its original aspect ratio. * @throws IOException If bitmap decoding or scaling fails. */ public PhotoProcessor(byte[] originalBytes, int maxDisplayPhotoDim, int maxThumbnailPhotoDim, boolean forceCropToSquare) throws IOException { this(BitmapFactory.decodeByteArray(originalBytes, 0, originalBytes.length), maxDisplayPhotoDim, maxThumbnailPhotoDim, forceCropToSquare); } /** * Processes the original image, producing a scaled-down display photo and thumbnail photo. * @throws IOException If bitmap decoding or scaling fails. */ private void process() throws IOException { if (mOriginal == null) { throw new IOException("Invalid image file"); } mDisplayPhoto = getScaledBitmap(mMaxDisplayPhotoDim); mThumbnailPhoto = getScaledBitmap(mMaxThumbnailPhotoDim); } /** * Scales down the original bitmap to fit within the given maximum width and height. * If the bitmap already fits in those dimensions, the original bitmap will be * returned unmodified unless the photo processor is set up to crop it to a square. * @param maxDim Maximum width and height (in pixels) for the image. * @return A bitmap that fits the maximum dimensions. */ @SuppressWarnings({"SuspiciousNameCombination"}) private Bitmap getScaledBitmap(int maxDim) { Bitmap scaledBitmap = mOriginal; int width = mOriginal.getWidth(); int height = mOriginal.getHeight(); int cropLeft = 0; int cropTop = 0; if (mForceCropToSquare && width != height) { // Crop the image to the square at its center. if (height > width) { cropTop = (height - width) / 2; height = width; } else { cropLeft = (width - height) / 2; width = height; } } float scaleFactor = ((float) maxDim) / Math.max(width, height); if (scaleFactor < 1.0 || cropLeft != 0 || cropTop != 0) { // Need to scale or crop the photo. Matrix matrix = new Matrix(); matrix.setScale(scaleFactor, scaleFactor); scaledBitmap = Bitmap.createBitmap( mOriginal, cropLeft, cropTop, width, height, matrix, false); } return scaledBitmap; } /** * Helper method to compress the given bitmap as a JPEG and return the resulting byte array. */ private byte[] getCompressedBytes(Bitmap b) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); boolean compressed = b.compress(Bitmap.CompressFormat.JPEG, 95, baos); if (!compressed) { throw new IOException("Unable to compress image"); } baos.flush(); baos.close(); return baos.toByteArray(); } /** * Retrieves the uncompressed display photo. */ public Bitmap getDisplayPhoto() { return mDisplayPhoto; } /** * Retrieves the uncompressed thumbnail photo. */ public Bitmap getThumbnailPhoto() { return mThumbnailPhoto; } /** * Retrieves the compressed display photo as a byte array. */ public byte[] getDisplayPhotoBytes() throws IOException { return getCompressedBytes(mDisplayPhoto); } /** * Retrieves the compressed thumbnail photo as a byte array. */ public byte[] getThumbnailPhotoBytes() throws IOException { return getCompressedBytes(mThumbnailPhoto); } /** * Retrieves the maximum width or height (in pixels) of the display photo. */ public int getMaxDisplayPhotoDim() { return mMaxDisplayPhotoDim; } /** * Retrieves the maximum width or height (in pixels) of the thumbnail. */ public int getMaxThumbnailPhotoDim() { return mMaxThumbnailPhotoDim; } }