Skip to content

Most visited

Recently visited

navigation
DisplayingBitmaps / src / com.example.android.displayingbitmaps / util /

ImageResizer.java

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.displayingbitmaps.util;
18
 
19
import android.annotation.TargetApi;
20
import android.content.Context;
21
import android.content.res.Resources;
22
import android.graphics.Bitmap;
23
import android.graphics.BitmapFactory;
24
import android.os.Build;
25
 
26
import com.example.android.common.logger.Log;
27
import com.example.android.displayingbitmaps.BuildConfig;
28
 
29
import java.io.FileDescriptor;
30
 
31
/**
32
 * A simple subclass of {@link ImageWorker} that resizes images from resources given a target width
33
 * and height. Useful for when the input images might be too large to simply load directly into
34
 * memory.
35
 */
36
public class ImageResizer extends ImageWorker {
37
    private static final String TAG = "ImageResizer";
38
    protected int mImageWidth;
39
    protected int mImageHeight;
40
 
41
    /**
42
     * Initialize providing a single target image size (used for both width and height);
43
     *
44
     * @param context
45
     * @param imageWidth
46
     * @param imageHeight
47
     */
48
    public ImageResizer(Context context, int imageWidth, int imageHeight) {
49
        super(context);
50
        setImageSize(imageWidth, imageHeight);
51
    }
52
 
53
    /**
54
     * Initialize providing a single target image size (used for both width and height);
55
     *
56
     * @param context
57
     * @param imageSize
58
     */
59
    public ImageResizer(Context context, int imageSize) {
60
        super(context);
61
        setImageSize(imageSize);
62
    }
63
 
64
    /**
65
     * Set the target image width and height.
66
     *
67
     * @param width
68
     * @param height
69
     */
70
    public void setImageSize(int width, int height) {
71
        mImageWidth = width;
72
        mImageHeight = height;
73
    }
74
 
75
    /**
76
     * Set the target image size (width and height will be the same).
77
     *
78
     * @param size
79
     */
80
    public void setImageSize(int size) {
81
        setImageSize(size, size);
82
    }
83
 
84
    /**
85
     * The main processing method. This happens in a background task. In this case we are just
86
     * sampling down the bitmap and returning it from a resource.
87
     *
88
     * @param resId
89
     * @return
90
     */
91
    private Bitmap processBitmap(int resId) {
92
        if (BuildConfig.DEBUG) {
93
            Log.d(TAG, "processBitmap - " + resId);
94
        }
95
        return decodeSampledBitmapFromResource(mResources, resId, mImageWidth,
96
                mImageHeight, getImageCache());
97
    }
98
 
99
    @Override
100
    protected Bitmap processBitmap(Object data) {
101
        return processBitmap(Integer.parseInt(String.valueOf(data)));
102
    }
103
 
104
    /**
105
     * Decode and sample down a bitmap from resources to the requested width and height.
106
     *
107
     * @param res The resources object containing the image data
108
     * @param resId The resource id of the image data
109
     * @param reqWidth The requested width of the resulting bitmap
110
     * @param reqHeight The requested height of the resulting bitmap
111
     * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
112
     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
113
     *         that are equal to or greater than the requested width and height
114
     */
115
    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
116
            int reqWidth, int reqHeight, ImageCache cache) {
117
 
119
        // First decode with inJustDecodeBounds=true to check dimensions
120
        final BitmapFactory.Options options = new BitmapFactory.Options();
121
        options.inJustDecodeBounds = true;
122
        BitmapFactory.decodeResource(res, resId, options);
123
 
124
        // Calculate inSampleSize
125
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
127
 
128
        // If we're running on Honeycomb or newer, try to use inBitmap
129
        if (Utils.hasHoneycomb()) {
130
            addInBitmapOptions(options, cache);
131
        }
132
 
133
        // Decode bitmap with inSampleSize set
134
        options.inJustDecodeBounds = false;
135
        return BitmapFactory.decodeResource(res, resId, options);
136
    }
137
 
138
    /**
139
     * Decode and sample down a bitmap from a file to the requested width and height.
140
     *
141
     * @param filename The full path of the file to decode
142
     * @param reqWidth The requested width of the resulting bitmap
143
     * @param reqHeight The requested height of the resulting bitmap
144
     * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
145
     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
146
     *         that are equal to or greater than the requested width and height
147
     */
148
    public static Bitmap decodeSampledBitmapFromFile(String filename,
149
            int reqWidth, int reqHeight, ImageCache cache) {
150
 
151
        // First decode with inJustDecodeBounds=true to check dimensions
152
        final BitmapFactory.Options options = new BitmapFactory.Options();
153
        options.inJustDecodeBounds = true;
154
        BitmapFactory.decodeFile(filename, options);
155
 
156
        // Calculate inSampleSize
157
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
158
 
159
        // If we're running on Honeycomb or newer, try to use inBitmap
160
        if (Utils.hasHoneycomb()) {
161
            addInBitmapOptions(options, cache);
162
        }
163
 
164
        // Decode bitmap with inSampleSize set
165
        options.inJustDecodeBounds = false;
166
        return BitmapFactory.decodeFile(filename, options);
167
    }
168
 
169
    /**
170
     * Decode and sample down a bitmap from a file input stream to the requested width and height.
171
     *
172
     * @param fileDescriptor The file descriptor to read from
173
     * @param reqWidth The requested width of the resulting bitmap
174
     * @param reqHeight The requested height of the resulting bitmap
175
     * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
176
     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
177
     *         that are equal to or greater than the requested width and height
178
     */
179
    public static Bitmap decodeSampledBitmapFromDescriptor(
180
            FileDescriptor fileDescriptor, int reqWidth, int reqHeight, ImageCache cache) {
181
 
182
        // First decode with inJustDecodeBounds=true to check dimensions
183
        final BitmapFactory.Options options = new BitmapFactory.Options();
184
        options.inJustDecodeBounds = true;
185
        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
186
 
187
        // Calculate inSampleSize
188
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
189
 
190
        // Decode bitmap with inSampleSize set
191
        options.inJustDecodeBounds = false;
192
 
193
        // If we're running on Honeycomb or newer, try to use inBitmap
194
        if (Utils.hasHoneycomb()) {
195
            addInBitmapOptions(options, cache);
196
        }
197
 
198
        return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
199
    }
200
 
201
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
202
    private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) {
204
        // inBitmap only works with mutable bitmaps so force the decoder to
205
        // return mutable bitmaps.
206
        options.inMutable = true;
207
 
208
        if (cache != null) {
209
            // Try and find a bitmap to use for inBitmap
210
            Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
211
 
212
            if (inBitmap != null) {
213
                options.inBitmap = inBitmap;
214
            }
215
        }
217
    }
218
 
219
    /**
220
     * Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options} object when decoding
221
     * bitmaps using the decode* methods from {@link android.graphics.BitmapFactory}. This implementation calculates
222
     * the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap
223
     * having a width and height equal to or larger than the requested width and height.
224
     *
225
     * @param options An options object with out* params already populated (run through a decode*
226
     *            method with inJustDecodeBounds==true
227
     * @param reqWidth The requested width of the resulting bitmap
228
     * @param reqHeight The requested height of the resulting bitmap
229
     * @return The value to be used for inSampleSize
230
     */
231
    public static int calculateInSampleSize(BitmapFactory.Options options,
232
            int reqWidth, int reqHeight) {
234
        // Raw height and width of image
235
        final int height = options.outHeight;
236
        final int width = options.outWidth;
237
        int inSampleSize = 1;
238
 
239
        if (height > reqHeight || width > reqWidth) {
240
 
241
            final int halfHeight = height / 2;
242
            final int halfWidth = width / 2;
243
 
244
            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
245
            // height and width larger than the requested height and width.
246
            while ((halfHeight / inSampleSize) > reqHeight
247
                    && (halfWidth / inSampleSize) > reqWidth) {
248
                inSampleSize *= 2;
249
            }
250
 
251
            // This offers some additional logic in case the image has a strange
252
            // aspect ratio. For example, a panorama may have a much larger
253
            // width than height. In these cases the total pixels might still
254
            // end up being too large to fit comfortably in memory, so we should
255
            // be more aggressive with sample down the image (=larger inSampleSize).
256
 
257
            long totalPixels = width * height / inSampleSize;
258
 
259
            // Anything more than 2x the requested pixels we'll sample down further
260
            final long totalReqPixelsCap = reqWidth * reqHeight * 2;
261
 
262
            while (totalPixels > totalReqPixelsCap) {
263
                inSampleSize *= 2;
264
                totalPixels /= 2;
265
            }
266
        }
267
        return inSampleSize;
269
    }
270
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.