• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 2010 The Android Open Source Project
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package android.graphics;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.res.AssetManager;
22 import android.os.Build;
23 import android.os.ParcelFileDescriptor;
24 
25 import java.io.FileDescriptor;
26 import java.io.FileInputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 
30 /**
31  * BitmapRegionDecoder can be used to decode a rectangle region from an image.
32  * BitmapRegionDecoder is particularly useful when an original image is large and
33  * you only need parts of the image.
34  *
35  * <p>To create a BitmapRegionDecoder, call newInstance(...).
36  * Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly
37  * to get a decoded Bitmap of the specified region.
38  *
39  */
40 public final class BitmapRegionDecoder {
41     private long mNativeBitmapRegionDecoder;
42     private boolean mRecycled;
43     // ensures that the native decoder object exists and that only one decode can
44     // occur at a time.
45     private final Object mNativeLock = new Object();
46 
47     /**
48      * Create a BitmapRegionDecoder from the specified byte array.
49      * Currently only the JPEG, PNG, WebP and HEIF formats are supported.
50      *
51      * @param data byte array of compressed image data.
52      * @param offset offset into data for where the decoder should begin
53      *               parsing.
54      * @param length the number of bytes, beginning at offset, to parse
55      * @param isShareable This field has been ignored since
56      *                    {@link Build.VERSION_CODES#GINGERBREAD}.
57      * @throws IOException if the image format is not supported or can not be decoded.
58      * @deprecated In favor of {@link #newInstance(byte[], int, int)}
59      */
60     @Deprecated
61     @NonNull
newInstance(@onNull byte[] data, int offset, int length, boolean isShareable)62     public static BitmapRegionDecoder newInstance(@NonNull byte[] data,
63             int offset, int length, boolean isShareable) throws IOException {
64         return newInstance(data, offset, length);
65     }
66 
67     /**
68      * Create a BitmapRegionDecoder from the specified byte array.
69      * Currently only the JPEG, PNG, WebP and HEIF formats are supported.
70      *
71      * @param data byte array of compressed image data.
72      * @param offset offset into data for where the decoder should begin
73      *               parsing.
74      * @param length the number of bytes, beginning at offset, to parse
75      * @throws IOException if the image format is not supported or can not be decoded.
76      */
77     @NonNull
newInstance(@onNull byte[] data, int offset, int length)78     public static BitmapRegionDecoder newInstance(@NonNull byte[] data,
79             int offset, int length) throws IOException {
80         if ((offset | length) < 0 || data.length < offset + length) {
81             throw new ArrayIndexOutOfBoundsException();
82         }
83         return nativeNewInstance(data, offset, length);
84     }
85 
86     /**
87      * Create a BitmapRegionDecoder from the file descriptor.
88      * The position within the descriptor will not be changed when
89      * this returns, so the descriptor can be used again as is.
90      * Currently only the JPEG, PNG, WebP and HEIF formats are supported.
91      *
92      * @param fd The file descriptor containing the data to decode
93      * @param isShareable This field has been ignored since
94      *                    {@link Build.VERSION_CODES#KITKAT}.
95      * @throws IOException if the image format is not supported or can not be decoded.
96      * @deprecated In favor of {@link #newInstance(ParcelFileDescriptor)}
97      */
98     @Deprecated
99     @NonNull
newInstance( @onNull FileDescriptor fd, boolean isShareable)100     public static BitmapRegionDecoder newInstance(
101             @NonNull FileDescriptor fd, boolean isShareable) throws IOException {
102         return nativeNewInstance(fd);
103     }
104 
105     /**
106      * Create a BitmapRegionDecoder from the file descriptor.
107      * The position within the descriptor will not be changed when
108      * this returns, so the descriptor can be used again as is.
109      * Currently only the JPEG, PNG, WebP and HEIF formats are supported.
110      *
111      * @param pfd The parcel file descriptor containing the data to decode
112      * @throws IOException if the image format is not supported or can not be decoded.
113      */
114     @NonNull
newInstance( @onNull ParcelFileDescriptor pfd)115     public static BitmapRegionDecoder newInstance(
116             @NonNull ParcelFileDescriptor pfd) throws IOException {
117         return nativeNewInstance(pfd.getFileDescriptor());
118     }
119 
120     /**
121      * Create a BitmapRegionDecoder from an input stream.
122      * The stream's position will be where ever it was after the encoded data
123      * was read.
124      * Currently only the JPEG, PNG, WebP and HEIF formats are supported.
125      *
126      * @param is The input stream that holds the raw data to be decoded into a
127      *           BitmapRegionDecoder.
128      * @param isShareable This field has always been ignored.
129      * @return A new BitmapRegionDecoder, or {@code null} if {@code is} is {@code null}.
130      * @throws IOException if the image format is not supported or can not be decoded.
131      * @deprecated In favor of {@link #newInstance(InputStream)}
132      *
133      * <p class="note">Prior to {@link Build.VERSION_CODES#KITKAT},
134      * if {@link InputStream#markSupported is.markSupported()} returns true,
135      * <code>is.mark(1024)</code> would be called. As of
136      * {@link Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
137      */
138     @Deprecated
139     @Nullable
newInstance(@onNull InputStream is, boolean isShareable)140     public static BitmapRegionDecoder newInstance(@NonNull InputStream is,
141             boolean isShareable) throws IOException {
142         return newInstance(is);
143     }
144 
145     /**
146      * Create a BitmapRegionDecoder from an input stream.
147      * The stream's position will be where ever it was after the encoded data
148      * was read.
149      * Currently only the JPEG, PNG, WebP and HEIF formats are supported.
150      *
151      * @param is The input stream that holds the raw data to be decoded into a
152      *           BitmapRegionDecoder.
153      * @return A new BitmapRegionDecoder, or {@code null} if {@code is} is {@code null}.
154      * @throws IOException if the image format is not supported or can not be decoded.
155      */
156     @Nullable
newInstance(@onNull InputStream is)157     public static BitmapRegionDecoder newInstance(@NonNull InputStream is) throws IOException {
158         if (is instanceof AssetManager.AssetInputStream) {
159             return nativeNewInstance(
160                     ((AssetManager.AssetInputStream) is).getNativeAsset());
161         } else {
162             // pass some temp storage down to the native code. 1024 is made up,
163             // but should be large enough to avoid too many small calls back
164             // into is.read(...).
165             byte [] tempStorage = new byte[16 * 1024];
166             return nativeNewInstance(is, tempStorage);
167         }
168     }
169 
170     /**
171      * Create a BitmapRegionDecoder from a file path.
172      * Currently only the JPEG, PNG, WebP and HEIF formats are supported.
173      *
174      * @param pathName complete path name for the file to be decoded.
175      * @param isShareable This field has always been ignored.
176      * @throws IOException if the image format is not supported or can not be decoded.
177      * @deprecated In favor of {@link #newInstance(String)}
178      */
179     @Deprecated
180     @NonNull
newInstance(@onNull String pathName, boolean isShareable)181     public static BitmapRegionDecoder newInstance(@NonNull String pathName,
182             boolean isShareable) throws IOException {
183         return newInstance(pathName);
184     }
185 
186     /**
187      * Create a BitmapRegionDecoder from a file path.
188      * Currently only the JPEG, PNG, WebP and HEIF formats are supported.
189      *
190      * @param pathName complete path name for the file to be decoded.
191      * @throws IOException if the image format is not supported or can not be decoded.
192      */
193     @NonNull
newInstance(@onNull String pathName)194     public static BitmapRegionDecoder newInstance(@NonNull String pathName) throws IOException {
195         BitmapRegionDecoder decoder = null;
196         InputStream stream = null;
197 
198         try {
199             stream = new FileInputStream(pathName);
200             decoder = newInstance(stream);
201         } finally {
202             if (stream != null) {
203                 try {
204                     stream.close();
205                 } catch (IOException e) {
206                     // do nothing here
207                 }
208             }
209         }
210         return decoder;
211     }
212 
213     /*  Private constructor that must receive an already allocated native
214         region decoder int (pointer).
215 
216         This can be called from JNI code.
217     */
218     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
BitmapRegionDecoder(long decoder)219     private BitmapRegionDecoder(long decoder) {
220         mNativeBitmapRegionDecoder = decoder;
221         mRecycled = false;
222     }
223 
224     /**
225      * Decodes a rectangle region in the image specified by rect.
226      *
227      * @param rect The rectangle that specified the region to be decode.
228      * @param options null-ok; Options that control downsampling.
229      *             inPurgeable is not supported.
230      * @return The decoded bitmap, or null if the image data could not be
231      *         decoded.
232      * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
233      *         is {@link android.graphics.Bitmap.Config#HARDWARE}
234      *         and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
235      *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
236      *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
237      */
decodeRegion(Rect rect, BitmapFactory.Options options)238     public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
239         BitmapFactory.Options.validate(options);
240         synchronized (mNativeLock) {
241             checkRecycled("decodeRegion called on recycled region decoder");
242             if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= getWidth()
243                     || rect.top >= getHeight())
244                 throw new IllegalArgumentException("rectangle is outside the image");
245             return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
246                     rect.right - rect.left, rect.bottom - rect.top, options,
247                     BitmapFactory.Options.nativeInBitmap(options),
248                     BitmapFactory.Options.nativeColorSpace(options));
249         }
250     }
251 
252     /** Returns the original image's width */
getWidth()253     public int getWidth() {
254         synchronized (mNativeLock) {
255             checkRecycled("getWidth called on recycled region decoder");
256             return nativeGetWidth(mNativeBitmapRegionDecoder);
257         }
258     }
259 
260     /** Returns the original image's height */
getHeight()261     public int getHeight() {
262         synchronized (mNativeLock) {
263             checkRecycled("getHeight called on recycled region decoder");
264             return nativeGetHeight(mNativeBitmapRegionDecoder);
265         }
266     }
267 
268     /**
269      * Frees up the memory associated with this region decoder, and mark the
270      * region decoder as "dead", meaning it will throw an exception if decodeRegion(),
271      * getWidth() or getHeight() is called.
272      *
273      * <p>This operation cannot be reversed, so it should only be called if you are
274      * sure there are no further uses for the region decoder. This is an advanced call,
275      * and normally need not be called, since the normal GC process will free up this
276      * memory when there are no more references to this region decoder.
277      */
recycle()278     public void recycle() {
279         synchronized (mNativeLock) {
280             if (!mRecycled) {
281                 nativeClean(mNativeBitmapRegionDecoder);
282                 mRecycled = true;
283             }
284         }
285     }
286 
287     /**
288      * Returns true if this region decoder has been recycled.
289      * If so, then it is an error to try use its method.
290      *
291      * @return true if the region decoder has been recycled
292      */
isRecycled()293     public final boolean isRecycled() {
294         return mRecycled;
295     }
296 
297     /**
298      * Called by methods that want to throw an exception if the region decoder
299      * has already been recycled.
300      */
checkRecycled(String errorMessage)301     private void checkRecycled(String errorMessage) {
302         if (mRecycled) {
303             throw new IllegalStateException(errorMessage);
304         }
305     }
306 
307     @Override
finalize()308     protected void finalize() throws Throwable {
309         try {
310             recycle();
311         } finally {
312             super.finalize();
313         }
314     }
315 
nativeDecodeRegion(long lbm, int start_x, int start_y, int width, int height, BitmapFactory.Options options, long inBitmapHandle, long colorSpaceHandle)316     private static native Bitmap nativeDecodeRegion(long lbm,
317             int start_x, int start_y, int width, int height,
318             BitmapFactory.Options options, long inBitmapHandle,
319             long colorSpaceHandle);
nativeGetWidth(long lbm)320     private static native int nativeGetWidth(long lbm);
nativeGetHeight(long lbm)321     private static native int nativeGetHeight(long lbm);
nativeClean(long lbm)322     private static native void nativeClean(long lbm);
323 
nativeNewInstance( byte[] data, int offset, int length)324     private static native BitmapRegionDecoder nativeNewInstance(
325             byte[] data, int offset, int length);
nativeNewInstance( FileDescriptor fd)326     private static native BitmapRegionDecoder nativeNewInstance(
327             FileDescriptor fd);
nativeNewInstance( InputStream is, byte[] storage)328     private static native BitmapRegionDecoder nativeNewInstance(
329             InputStream is, byte[] storage);
nativeNewInstance( long asset)330     private static native BitmapRegionDecoder nativeNewInstance(
331             long asset);
332 }
333