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