• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.example.android.bitmapfun.util;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.util.Log;
24 
25 import com.example.android.bitmapfun.BuildConfig;
26 
27 import java.io.FileDescriptor;
28 
29 /**
30  * A simple subclass of {@link ImageWorker} that resizes images from resources given a target width
31  * and height. Useful for when the input images might be too large to simply load directly into
32  * memory.
33  */
34 public class ImageResizer extends ImageWorker {
35     private static final String TAG = "ImageResizer";
36     protected int mImageWidth;
37     protected int mImageHeight;
38 
39     /**
40      * Initialize providing a single target image size (used for both width and height);
41      *
42      * @param context
43      * @param imageWidth
44      * @param imageHeight
45      */
ImageResizer(Context context, int imageWidth, int imageHeight)46     public ImageResizer(Context context, int imageWidth, int imageHeight) {
47         super(context);
48         setImageSize(imageWidth, imageHeight);
49     }
50 
51     /**
52      * Initialize providing a single target image size (used for both width and height);
53      *
54      * @param context
55      * @param imageSize
56      */
ImageResizer(Context context, int imageSize)57     public ImageResizer(Context context, int imageSize) {
58         super(context);
59         setImageSize(imageSize);
60     }
61 
62     /**
63      * Set the target image width and height.
64      *
65      * @param width
66      * @param height
67      */
setImageSize(int width, int height)68     public void setImageSize(int width, int height) {
69         mImageWidth = width;
70         mImageHeight = height;
71     }
72 
73     /**
74      * Set the target image size (width and height will be the same).
75      *
76      * @param size
77      */
setImageSize(int size)78     public void setImageSize(int size) {
79         setImageSize(size, size);
80     }
81 
82     /**
83      * The main processing method. This happens in a background task. In this case we are just
84      * sampling down the bitmap and returning it from a resource.
85      *
86      * @param resId
87      * @return
88      */
processBitmap(int resId)89     private Bitmap processBitmap(int resId) {
90         if (BuildConfig.DEBUG) {
91             Log.d(TAG, "processBitmap - " + resId);
92         }
93         return decodeSampledBitmapFromResource(mResources, resId, mImageWidth, mImageHeight);
94     }
95 
96     @Override
processBitmap(Object data)97     protected Bitmap processBitmap(Object data) {
98         return processBitmap(Integer.parseInt(String.valueOf(data)));
99     }
100 
101     /**
102      * Decode and sample down a bitmap from resources to the requested width and height.
103      *
104      * @param res The resources object containing the image data
105      * @param resId The resource id of the image data
106      * @param reqWidth The requested width of the resulting bitmap
107      * @param reqHeight The requested height of the resulting bitmap
108      * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
109      *         that are equal to or greater than the requested width and height
110      */
decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight)111     public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
112             int reqWidth, int reqHeight) {
113 
114         // First decode with inJustDecodeBounds=true to check dimensions
115         final BitmapFactory.Options options = new BitmapFactory.Options();
116         options.inJustDecodeBounds = true;
117         BitmapFactory.decodeResource(res, resId, options);
118 
119         // Calculate inSampleSize
120         options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
121 
122         // Decode bitmap with inSampleSize set
123         options.inJustDecodeBounds = false;
124         return BitmapFactory.decodeResource(res, resId, options);
125     }
126 
127     /**
128      * Decode and sample down a bitmap from a file to the requested width and height.
129      *
130      * @param filename The full path of the file to decode
131      * @param reqWidth The requested width of the resulting bitmap
132      * @param reqHeight The requested height of the resulting bitmap
133      * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
134      *         that are equal to or greater than the requested width and height
135      */
decodeSampledBitmapFromFile(String filename, int reqWidth, int reqHeight)136     public static Bitmap decodeSampledBitmapFromFile(String filename,
137             int reqWidth, int reqHeight) {
138 
139         // First decode with inJustDecodeBounds=true to check dimensions
140         final BitmapFactory.Options options = new BitmapFactory.Options();
141         options.inJustDecodeBounds = true;
142         BitmapFactory.decodeFile(filename, options);
143 
144         // Calculate inSampleSize
145         options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
146 
147         // Decode bitmap with inSampleSize set
148         options.inJustDecodeBounds = false;
149         return BitmapFactory.decodeFile(filename, options);
150     }
151 
152     /**
153      * Decode and sample down a bitmap from a file input stream to the requested width and height.
154      *
155      * @param fileDescriptor The file descriptor to read from
156      * @param reqWidth The requested width of the resulting bitmap
157      * @param reqHeight The requested height of the resulting bitmap
158      * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
159      *         that are equal to or greater than the requested width and height
160      */
decodeSampledBitmapFromDescriptor( FileDescriptor fileDescriptor, int reqWidth, int reqHeight)161     public static Bitmap decodeSampledBitmapFromDescriptor(
162             FileDescriptor fileDescriptor, int reqWidth, int reqHeight) {
163 
164         // First decode with inJustDecodeBounds=true to check dimensions
165         final BitmapFactory.Options options = new BitmapFactory.Options();
166         options.inJustDecodeBounds = true;
167         BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
168 
169         // Calculate inSampleSize
170         options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
171 
172         // Decode bitmap with inSampleSize set
173         options.inJustDecodeBounds = false;
174         return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
175     }
176 
177     /**
178      * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
179      * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
180      * the closest inSampleSize that will result in the final decoded bitmap having a width and
181      * height equal to or larger than the requested width and height. This implementation does not
182      * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
183      * results in a larger bitmap which isn't as useful for caching purposes.
184      *
185      * @param options An options object with out* params already populated (run through a decode*
186      *            method with inJustDecodeBounds==true
187      * @param reqWidth The requested width of the resulting bitmap
188      * @param reqHeight The requested height of the resulting bitmap
189      * @return The value to be used for inSampleSize
190      */
calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)191     public static int calculateInSampleSize(BitmapFactory.Options options,
192             int reqWidth, int reqHeight) {
193         // Raw height and width of image
194         final int height = options.outHeight;
195         final int width = options.outWidth;
196         int inSampleSize = 1;
197 
198         if (height > reqHeight || width > reqWidth) {
199 
200             // Calculate ratios of height and width to requested height and width
201             final int heightRatio = Math.round((float) height / (float) reqHeight);
202             final int widthRatio = Math.round((float) width / (float) reqWidth);
203 
204             // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
205             // with both dimensions larger than or equal to the requested height and width.
206             inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
207 
208             // This offers some additional logic in case the image has a strange
209             // aspect ratio. For example, a panorama may have a much larger
210             // width than height. In these cases the total pixels might still
211             // end up being too large to fit comfortably in memory, so we should
212             // be more aggressive with sample down the image (=larger inSampleSize).
213 
214             final float totalPixels = width * height;
215 
216             // Anything more than 2x the requested pixels we'll sample down further
217             final float totalReqPixelsCap = reqWidth * reqHeight * 2;
218 
219             while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
220                 inSampleSize++;
221             }
222         }
223         return inSampleSize;
224     }
225 }
226