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