Chiao Cheng | 8661800 | 2012-10-16 13:21:10 -0700 | [diff] [blame] | 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 | |
Gary Mai | 69c182a | 2016-12-05 13:07:03 -0800 | [diff] [blame] | 17 | package com.android.contacts.util; |
Chiao Cheng | 8661800 | 2012-10-16 13:21:10 -0700 | [diff] [blame] | 18 | |
| 19 | import android.graphics.Bitmap; |
| 20 | import android.graphics.BitmapFactory; |
Paul Soulos | 24dad55 | 2014-06-12 13:34:49 -0700 | [diff] [blame] | 21 | import android.graphics.Canvas; |
Yorke Lee | e625b22 | 2014-10-17 17:20:19 -0700 | [diff] [blame] | 22 | import android.graphics.Paint; |
| 23 | import android.graphics.PorterDuff.Mode; |
| 24 | import android.graphics.PorterDuffXfermode; |
| 25 | import android.graphics.Rect; |
| 26 | import android.graphics.RectF; |
Paul Soulos | 24dad55 | 2014-06-12 13:34:49 -0700 | [diff] [blame] | 27 | import android.graphics.drawable.BitmapDrawable; |
Gary Mai | 0a49afa | 2016-12-05 15:53:58 -0800 | [diff] [blame] | 28 | import android.graphics.drawable.Drawable; |
Chiao Cheng | 8661800 | 2012-10-16 13:21:10 -0700 | [diff] [blame] | 29 | |
| 30 | /** |
| 31 | * Provides static functions to decode bitmaps at the optimal size |
| 32 | */ |
| 33 | public class BitmapUtil { |
| 34 | private BitmapUtil() {} |
| 35 | |
| 36 | /** |
| 37 | * Returns Width or Height of the picture, depending on which size is smaller. Doesn't actually |
| 38 | * decode the picture, so it is pretty efficient to run. |
| 39 | */ |
| 40 | public static int getSmallerExtentFromBytes(byte[] bytes) { |
| 41 | final BitmapFactory.Options options = new BitmapFactory.Options(); |
| 42 | |
| 43 | // don't actually decode the picture, just return its bounds |
| 44 | options.inJustDecodeBounds = true; |
| 45 | BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); |
| 46 | |
| 47 | // test what the best sample size is |
| 48 | return Math.min(options.outWidth, options.outHeight); |
| 49 | } |
| 50 | |
| 51 | /** |
| 52 | * Finds the optimal sampleSize for loading the picture |
| 53 | * @param originalSmallerExtent Width or height of the picture, whichever is smaller |
| 54 | * @param targetExtent Width or height of the target view, whichever is bigger. |
| 55 | * |
| 56 | * If either one of the parameters is 0 or smaller, no sampling is applied |
| 57 | */ |
| 58 | public static int findOptimalSampleSize(int originalSmallerExtent, int targetExtent) { |
| 59 | // If we don't know sizes, we can't do sampling. |
| 60 | if (targetExtent < 1) return 1; |
| 61 | if (originalSmallerExtent < 1) return 1; |
| 62 | |
| 63 | // Test what the best sample size is. To do that, we find the sample size that gives us |
| 64 | // the best trade-off between resulting image size and memory requirement. We allow |
| 65 | // the down-sampled image to be 20% smaller than the target size. That way we can get around |
| 66 | // unfortunate cases where e.g. a 720 picture is requested for 362 and not down-sampled at |
| 67 | // all. Why 20%? Why not. Prove me wrong. |
| 68 | int extent = originalSmallerExtent; |
| 69 | int sampleSize = 1; |
| 70 | while ((extent >> 1) >= targetExtent * 0.8f) { |
| 71 | sampleSize <<= 1; |
| 72 | extent >>= 1; |
| 73 | } |
| 74 | |
| 75 | return sampleSize; |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * Decodes the bitmap with the given sample size |
| 80 | */ |
| 81 | public static Bitmap decodeBitmapFromBytes(byte[] bytes, int sampleSize) { |
| 82 | final BitmapFactory.Options options; |
| 83 | if (sampleSize <= 1) { |
| 84 | options = null; |
| 85 | } else { |
| 86 | options = new BitmapFactory.Options(); |
| 87 | options.inSampleSize = sampleSize; |
| 88 | } |
| 89 | return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); |
| 90 | } |
Paul Soulos | 24dad55 | 2014-06-12 13:34:49 -0700 | [diff] [blame] | 91 | |
| 92 | /** |
| 93 | * Retrieves a copy of the specified drawable resource, rotated by a specified angle. |
| 94 | * |
| 95 | * @param resources The current resources. |
| 96 | * @param resourceId The resource ID of the drawable to rotate. |
| 97 | * @param angle The angle of rotation. |
| 98 | * @return Rotated drawable. |
| 99 | */ |
| 100 | public static Drawable getRotatedDrawable( |
| 101 | android.content.res.Resources resources, int resourceId, float angle) { |
| 102 | |
| 103 | // Get the original drawable and make a copy which will be rotated. |
| 104 | Bitmap original = BitmapFactory.decodeResource(resources, resourceId); |
| 105 | Bitmap rotated = Bitmap.createBitmap( |
| 106 | original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888); |
| 107 | |
| 108 | // Perform the rotation. |
| 109 | Canvas tempCanvas = new Canvas(rotated); |
| 110 | tempCanvas.rotate(angle, original.getWidth()/2, original.getHeight()/2); |
| 111 | tempCanvas.drawBitmap(original, 0, 0, null); |
| 112 | |
| 113 | return new BitmapDrawable(resources,rotated); |
| 114 | } |
Yorke Lee | e625b22 | 2014-10-17 17:20:19 -0700 | [diff] [blame] | 115 | |
| 116 | /** |
| 117 | * Given an input bitmap, scales it to the given width/height and makes it round. |
| 118 | * |
| 119 | * @param input {@link Bitmap} to scale and crop |
| 120 | * @param targetWidth desired output width |
| 121 | * @param targetHeight desired output height |
| 122 | * @return output bitmap scaled to the target width/height and cropped to an oval. The |
| 123 | * cropping algorithm will try to fit as much of the input into the output as possible, |
| 124 | * while preserving the target width/height ratio. |
| 125 | */ |
| 126 | public static Bitmap getRoundedBitmap(Bitmap input, int targetWidth, int targetHeight) { |
| 127 | if (input == null) { |
| 128 | return null; |
| 129 | } |
Jay Shrauner | ec84f38 | 2015-03-10 16:10:45 -0700 | [diff] [blame] | 130 | final Bitmap.Config inputConfig = input.getConfig(); |
| 131 | final Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, |
| 132 | inputConfig != null ? inputConfig : Bitmap.Config.ARGB_8888); |
Yorke Lee | e625b22 | 2014-10-17 17:20:19 -0700 | [diff] [blame] | 133 | final Canvas canvas = new Canvas(result); |
| 134 | final Paint paint = new Paint(); |
| 135 | canvas.drawARGB(0, 0, 0, 0); |
| 136 | paint.setAntiAlias(true); |
Wenyi Wang | fe4048c | 2015-12-21 15:53:43 -0800 | [diff] [blame] | 137 | final RectF dst = new RectF(0, 0, targetWidth, targetHeight); |
| 138 | canvas.drawOval(dst, paint); |
Yorke Lee | e625b22 | 2014-10-17 17:20:19 -0700 | [diff] [blame] | 139 | |
| 140 | // Specifies that only pixels present in the destination (i.e. the drawn oval) should |
| 141 | // be overwritten with pixels from the input bitmap. |
| 142 | paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); |
| 143 | |
| 144 | final int inputWidth = input.getWidth(); |
| 145 | final int inputHeight = input.getHeight(); |
| 146 | |
| 147 | // Choose the largest scale factor that will fit inside the dimensions of the |
| 148 | // input bitmap. |
| 149 | final float scaleBy = Math.min((float) inputWidth / targetWidth, |
| 150 | (float) inputHeight / targetHeight); |
| 151 | |
| 152 | final int xCropAmountHalved = (int) (scaleBy * targetWidth / 2); |
| 153 | final int yCropAmountHalved = (int) (scaleBy * targetHeight / 2); |
| 154 | |
| 155 | final Rect src = new Rect( |
| 156 | inputWidth / 2 - xCropAmountHalved, |
| 157 | inputHeight / 2 - yCropAmountHalved, |
| 158 | inputWidth / 2 + xCropAmountHalved, |
| 159 | inputHeight / 2 + yCropAmountHalved); |
| 160 | |
Yorke Lee | e625b22 | 2014-10-17 17:20:19 -0700 | [diff] [blame] | 161 | canvas.drawBitmap(input, src, dst, paint); |
| 162 | return result; |
| 163 | } |
Chiao Cheng | 8661800 | 2012-10-16 13:21:10 -0700 | [diff] [blame] | 164 | } |