1ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/* 2ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Copyright (C) 2012 The Android Open Source Project 3ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 4ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License"); 5ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * you may not use this file except in compliance with the License. 6ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * You may obtain a copy of the License at 7ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 8ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * http://www.apache.org/licenses/LICENSE-2.0 9ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 10ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Unless required by applicable law or agreed to in writing, software 11ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS, 12ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * See the License for the specific language governing permissions and 14ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * limitations under the License. 15ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */ 16ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 179b4d9bca30470c65f3c97f965d1f21523a631299Eric Erfanianpackage com.android.dialer.contactphoto; 18ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 19ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.Bitmap; 20ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.BitmapFactory; 21ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.Canvas; 22ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.Paint; 23ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.PorterDuff.Mode; 24ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.PorterDuffXfermode; 25ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.Rect; 26ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.RectF; 27ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 28ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/** Provides static functions to decode bitmaps at the optimal size */ 29ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpublic class BitmapUtil { 30ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 31ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private BitmapUtil() {} 32ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 33ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian /** 34ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Returns Width or Height of the picture, depending on which size is smaller. Doesn't actually 35ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * decode the picture, so it is pretty efficient to run. 36ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */ 37ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public static int getSmallerExtentFromBytes(byte[] bytes) { 38ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final BitmapFactory.Options options = new BitmapFactory.Options(); 39ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 40ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // don't actually decode the picture, just return its bounds 41ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian options.inJustDecodeBounds = true; 42ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); 43ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 44ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // test what the best sample size is 45ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return Math.min(options.outWidth, options.outHeight); 46ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 47ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 48ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian /** 49ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Finds the optimal sampleSize for loading the picture 50ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 51ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * @param originalSmallerExtent Width or height of the picture, whichever is smaller 52ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * @param targetExtent Width or height of the target view, whichever is bigger. 53ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>If either one of the parameters is 0 or smaller, no sampling is applied 54ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */ 55ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public static int findOptimalSampleSize(int originalSmallerExtent, int targetExtent) { 56ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // If we don't know sizes, we can't do sampling. 57ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (targetExtent < 1) { 58ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return 1; 59ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 60ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (originalSmallerExtent < 1) { 61ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return 1; 62ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 63ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 64ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Test what the best sample size is. To do that, we find the sample size that gives us 65ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // the best trade-off between resulting image size and memory requirement. We allow 66ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // the down-sampled image to be 20% smaller than the target size. That way we can get around 67ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // unfortunate cases where e.g. a 720 picture is requested for 362 and not down-sampled at 68ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // all. Why 20%? Why not. Prove me wrong. 69ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int extent = originalSmallerExtent; 70ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int sampleSize = 1; 71ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian while ((extent >> 1) >= targetExtent * 0.8f) { 72ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian sampleSize <<= 1; 73ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian extent >>= 1; 74ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 75ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 76ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return sampleSize; 77ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 78ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 79ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian /** Decodes the bitmap with the given sample size */ 80ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public static Bitmap decodeBitmapFromBytes(byte[] bytes, int sampleSize) { 81ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final BitmapFactory.Options options; 82ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (sampleSize <= 1) { 83ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian options = null; 84ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else { 85ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian options = new BitmapFactory.Options(); 86ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian options.inSampleSize = sampleSize; 87ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 88ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); 89ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 90ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 91ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian /** 92ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Given an input bitmap, scales it to the given width/height and makes it round. 93ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 94ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * @param input {@link Bitmap} to scale and crop 95ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * @param targetWidth desired output width 96ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * @param targetHeight desired output height 97ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * @return output bitmap scaled to the target width/height and cropped to an oval. The cropping 98ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * algorithm will try to fit as much of the input into the output as possible, while 99ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * preserving the target width/height ratio. 100ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */ 101ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public static Bitmap getRoundedBitmap(Bitmap input, int targetWidth, int targetHeight) { 102ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (input == null) { 103ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return null; 104ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 105ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final Bitmap.Config inputConfig = input.getConfig(); 106ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final Bitmap result = 107ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Bitmap.createBitmap( 108ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian targetWidth, targetHeight, inputConfig != null ? inputConfig : Bitmap.Config.ARGB_8888); 109ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final Canvas canvas = new Canvas(result); 110ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final Paint paint = new Paint(); 111ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian canvas.drawARGB(0, 0, 0, 0); 112ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian paint.setAntiAlias(true); 113ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final RectF dst = new RectF(0, 0, targetWidth, targetHeight); 114ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian canvas.drawOval(dst, paint); 115ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 116ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Specifies that only pixels present in the destination (i.e. the drawn oval) should 117ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // be overwritten with pixels from the input bitmap. 118ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 119ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 120ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final int inputWidth = input.getWidth(); 121ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final int inputHeight = input.getHeight(); 122ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 123ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Choose the largest scale factor that will fit inside the dimensions of the 124ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // input bitmap. 125ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final float scaleBy = 126ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Math.min((float) inputWidth / targetWidth, (float) inputHeight / targetHeight); 127ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 128ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final int xCropAmountHalved = (int) (scaleBy * targetWidth / 2); 129ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final int yCropAmountHalved = (int) (scaleBy * targetHeight / 2); 130ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 131ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final Rect src = 132ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian new Rect( 133ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian inputWidth / 2 - xCropAmountHalved, 134ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian inputHeight / 2 - yCropAmountHalved, 135ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian inputWidth / 2 + xCropAmountHalved, 136ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian inputHeight / 2 + yCropAmountHalved); 137ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 138ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian canvas.drawBitmap(input, src, dst, paint); 139ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return result; 140ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 141ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian} 142