1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License 15 */ 16 package com.android.providers.contacts; 17 18 import android.graphics.Bitmap; 19 import android.graphics.BitmapFactory; 20 import android.graphics.Matrix; 21 22 import java.io.ByteArrayOutputStream; 23 import java.io.IOException; 24 25 /** 26 * Class that converts a bitmap (or byte array representing a bitmap) into a display 27 * photo and a thumbnail photo. 28 */ 29 /* package-protected */ final class PhotoProcessor { 30 31 private final int mMaxDisplayPhotoDim; 32 private final int mMaxThumbnailPhotoDim; 33 private final boolean mForceCropToSquare; 34 private final Bitmap mOriginal; 35 private Bitmap mDisplayPhoto; 36 private Bitmap mThumbnailPhoto; 37 38 /** 39 * Initializes a photo processor for the given bitmap. 40 * @param original The bitmap to process. 41 * @param maxDisplayPhotoDim The maximum height and width for the display photo. 42 * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo. 43 * @throws IOException If bitmap decoding or scaling fails. 44 */ 45 public PhotoProcessor(Bitmap original, int maxDisplayPhotoDim, int maxThumbnailPhotoDim) 46 throws IOException { 47 this(original, maxDisplayPhotoDim, maxThumbnailPhotoDim, false); 48 } 49 50 /** 51 * Initializes a photo processor for the given bitmap. 52 * @param originalBytes A byte array to decode into a bitmap to process. 53 * @param maxDisplayPhotoDim The maximum height and width for the display photo. 54 * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo. 55 * @throws IOException If bitmap decoding or scaling fails. 56 */ 57 public PhotoProcessor(byte[] originalBytes, int maxDisplayPhotoDim, int maxThumbnailPhotoDim) 58 throws IOException { 59 this(BitmapFactory.decodeByteArray(originalBytes, 0, originalBytes.length), 60 maxDisplayPhotoDim, maxThumbnailPhotoDim, false); 61 } 62 63 /** 64 * Initializes a photo processor for the given bitmap. 65 * @param original The bitmap to process. 66 * @param maxDisplayPhotoDim The maximum height and width for the display photo. 67 * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo. 68 * @param forceCropToSquare Whether to force the processed images to be square. If the source 69 * photo is not square, this will crop to the square at the center of the image's rectangle. 70 * If this is not set to true, the image will simply be downscaled to fit in the given 71 * dimensions, retaining its original aspect ratio. 72 * @throws IOException If bitmap decoding or scaling fails. 73 */ 74 public PhotoProcessor(Bitmap original, int maxDisplayPhotoDim, int maxThumbnailPhotoDim, 75 boolean forceCropToSquare) throws IOException { 76 mOriginal = original; 77 mMaxDisplayPhotoDim = maxDisplayPhotoDim; 78 mMaxThumbnailPhotoDim = maxThumbnailPhotoDim; 79 mForceCropToSquare = forceCropToSquare; 80 process(); 81 } 82 83 /** 84 * Initializes a photo processor for the given bitmap. 85 * @param originalBytes A byte array to decode into a bitmap to process. 86 * @param maxDisplayPhotoDim The maximum height and width for the display photo. 87 * @param maxThumbnailPhotoDim The maximum height and width for the thumbnail photo. 88 * @param forceCropToSquare Whether to force the processed images to be square. If the source 89 * photo is not square, this will crop to the square at the center of the image's rectangle. 90 * If this is not set to true, the image will simply be downscaled to fit in the given 91 * dimensions, retaining its original aspect ratio. 92 * @throws IOException If bitmap decoding or scaling fails. 93 */ 94 public PhotoProcessor(byte[] originalBytes, int maxDisplayPhotoDim, int maxThumbnailPhotoDim, 95 boolean forceCropToSquare) throws IOException { 96 this(BitmapFactory.decodeByteArray(originalBytes, 0, originalBytes.length), 97 maxDisplayPhotoDim, maxThumbnailPhotoDim, forceCropToSquare); 98 } 99 100 /** 101 * Processes the original image, producing a scaled-down display photo and thumbnail photo. 102 * @throws IOException If bitmap decoding or scaling fails. 103 */ 104 private void process() throws IOException { 105 if (mOriginal == null) { 106 throw new IOException("Invalid image file"); 107 } 108 mDisplayPhoto = getScaledBitmap(mMaxDisplayPhotoDim); 109 mThumbnailPhoto = getScaledBitmap(mMaxThumbnailPhotoDim); 110 } 111 112 /** 113 * Scales down the original bitmap to fit within the given maximum width and height. 114 * If the bitmap already fits in those dimensions, the original bitmap will be 115 * returned unmodified unless the photo processor is set up to crop it to a square. 116 * @param maxDim Maximum width and height (in pixels) for the image. 117 * @return A bitmap that fits the maximum dimensions. 118 */ 119 @SuppressWarnings({"SuspiciousNameCombination"}) 120 private Bitmap getScaledBitmap(int maxDim) { 121 Bitmap scaledBitmap = mOriginal; 122 int width = mOriginal.getWidth(); 123 int height = mOriginal.getHeight(); 124 int cropLeft = 0; 125 int cropTop = 0; 126 if (mForceCropToSquare && width != height) { 127 // Crop the image to the square at its center. 128 if (height > width) { 129 cropTop = (height - width) / 2; 130 height = width; 131 } else { 132 cropLeft = (width - height) / 2; 133 width = height; 134 } 135 } 136 float scaleFactor = ((float) maxDim) / Math.max(width, height); 137 if (scaleFactor < 1.0 || cropLeft != 0 || cropTop != 0) { 138 // Need to scale or crop the photo. 139 Matrix matrix = new Matrix(); 140 matrix.setScale(scaleFactor, scaleFactor); 141 scaledBitmap = Bitmap.createBitmap( 142 mOriginal, cropLeft, cropTop, width, height, matrix, false); 143 } 144 return scaledBitmap; 145 } 146 147 /** 148 * Helper method to compress the given bitmap as a JPEG and return the resulting byte array. 149 */ 150 private byte[] getCompressedBytes(Bitmap b) throws IOException { 151 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 152 boolean compressed = b.compress(Bitmap.CompressFormat.JPEG, 95, baos); 153 if (!compressed) { 154 throw new IOException("Unable to compress image"); 155 } 156 baos.flush(); 157 baos.close(); 158 return baos.toByteArray(); 159 } 160 161 /** 162 * Retrieves the uncompressed display photo. 163 */ 164 public Bitmap getDisplayPhoto() { 165 return mDisplayPhoto; 166 } 167 168 /** 169 * Retrieves the uncompressed thumbnail photo. 170 */ 171 public Bitmap getThumbnailPhoto() { 172 return mThumbnailPhoto; 173 } 174 175 /** 176 * Retrieves the compressed display photo as a byte array. 177 */ 178 public byte[] getDisplayPhotoBytes() throws IOException { 179 return getCompressedBytes(mDisplayPhoto); 180 } 181 182 /** 183 * Retrieves the compressed thumbnail photo as a byte array. 184 */ 185 public byte[] getThumbnailPhotoBytes() throws IOException { 186 return getCompressedBytes(mThumbnailPhoto); 187 } 188 189 /** 190 * Retrieves the maximum width or height (in pixels) of the display photo. 191 */ 192 public int getMaxDisplayPhotoDim() { 193 return mMaxDisplayPhotoDim; 194 } 195 196 /** 197 * Retrieves the maximum width or height (in pixels) of the thumbnail. 198 */ 199 public int getMaxThumbnailPhotoDim() { 200 return mMaxThumbnailPhotoDim; 201 } 202 } 203