blob: 5512962c0f529c0a72ae1d7c3c159c1275598e35 [file] [log] [blame]
Chiao Cheng86618002012-10-16 13:21:10 -07001/*
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 Mai69c182a2016-12-05 13:07:03 -080017package com.android.contacts.util;
Chiao Cheng86618002012-10-16 13:21:10 -070018
19import android.graphics.Bitmap;
20import android.graphics.BitmapFactory;
Paul Soulos24dad552014-06-12 13:34:49 -070021import android.graphics.Canvas;
Yorke Leee625b222014-10-17 17:20:19 -070022import android.graphics.Paint;
23import android.graphics.PorterDuff.Mode;
24import android.graphics.PorterDuffXfermode;
25import android.graphics.Rect;
26import android.graphics.RectF;
Paul Soulos24dad552014-06-12 13:34:49 -070027import android.graphics.drawable.BitmapDrawable;
Gary Mai0a49afa2016-12-05 15:53:58 -080028import android.graphics.drawable.Drawable;
Chiao Cheng86618002012-10-16 13:21:10 -070029
30/**
31 * Provides static functions to decode bitmaps at the optimal size
32 */
33public 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 Soulos24dad552014-06-12 13:34:49 -070091
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 Leee625b222014-10-17 17:20:19 -0700115
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 Shraunerec84f382015-03-10 16:10:45 -0700130 final Bitmap.Config inputConfig = input.getConfig();
131 final Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight,
132 inputConfig != null ? inputConfig : Bitmap.Config.ARGB_8888);
Yorke Leee625b222014-10-17 17:20:19 -0700133 final Canvas canvas = new Canvas(result);
134 final Paint paint = new Paint();
135 canvas.drawARGB(0, 0, 0, 0);
136 paint.setAntiAlias(true);
Wenyi Wangfe4048c2015-12-21 15:53:43 -0800137 final RectF dst = new RectF(0, 0, targetWidth, targetHeight);
138 canvas.drawOval(dst, paint);
Yorke Leee625b222014-10-17 17:20:19 -0700139
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 Leee625b222014-10-17 17:20:19 -0700161 canvas.drawBitmap(input, src, dst, paint);
162 return result;
163 }
Chiao Cheng86618002012-10-16 13:21:10 -0700164}