1 /*
2  * Copyright (C) 2007 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 android.graphics;
18 
19 import static android.graphics.BitmapFactory.Options.validate;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.content.res.AssetManager;
25 import android.content.res.Resources;
26 import android.os.Build;
27 import android.os.Trace;
28 import android.util.DisplayMetrics;
29 import android.util.Log;
30 import android.util.TypedValue;
31 
32 import java.io.FileDescriptor;
33 import java.io.FileInputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 
37 /**
38  * Creates Bitmap objects from various sources, including files, streams,
39  * and byte-arrays.
40  */
41 public class BitmapFactory {
42     private static final int DECODE_BUFFER_SIZE = 16 * 1024;
43 
44     public static class Options {
45         /**
46          * Create a default Options object, which if left unchanged will give
47          * the same result from the decoder as if null were passed.
48          */
Options()49         public Options() {
50             inScaled = true;
51             inPremultiplied = true;
52         }
53 
54         /**
55          * If set, decode methods that take the Options object will attempt to
56          * reuse this bitmap when loading content. If the decode operation
57          * cannot use this bitmap, the decode method will throw an
58          * {@link java.lang.IllegalArgumentException}. The
59          * current implementation necessitates that the reused bitmap be
60          * mutable, and the resulting reused bitmap will continue to remain
61          * mutable even when decoding a resource which would normally result in
62          * an immutable bitmap.</p>
63          *
64          * <p>You should still always use the returned Bitmap of the decode
65          * method and not assume that reusing the bitmap worked, due to the
66          * constraints outlined above and failure situations that can occur.
67          * Checking whether the return value matches the value of the inBitmap
68          * set in the Options structure will indicate if the bitmap was reused,
69          * but in all cases you should use the Bitmap returned by the decoding
70          * function to ensure that you are using the bitmap that was used as the
71          * decode destination.</p>
72          *
73          * <h3>Usage with BitmapFactory</h3>
74          *
75          * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, any
76          * mutable bitmap can be reused by {@link BitmapFactory} to decode any
77          * other bitmaps as long as the resulting {@link Bitmap#getByteCount()
78          * byte count} of the decoded bitmap is less than or equal to the {@link
79          * Bitmap#getAllocationByteCount() allocated byte count} of the reused
80          * bitmap. This can be because the intrinsic size is smaller, or its
81          * size post scaling (for density / sample size) is smaller.</p>
82          *
83          * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT}
84          * additional constraints apply: The image being decoded (whether as a
85          * resource or as a stream) must be in jpeg or png format. Only equal
86          * sized bitmaps are supported, with {@link #inSampleSize} set to 1.
87          * Additionally, the {@link android.graphics.Bitmap.Config
88          * configuration} of the reused bitmap will override the setting of
89          * {@link #inPreferredConfig}, if set.</p>
90          *
91          * <h3>Usage with BitmapRegionDecoder</h3>
92          *
93          * <p>BitmapRegionDecoder will draw its requested content into the Bitmap
94          * provided, clipping if the output content size (post scaling) is larger
95          * than the provided Bitmap. The provided Bitmap's width, height, and
96          * {@link Bitmap.Config} will not be changed.
97          *
98          * <p class="note">BitmapRegionDecoder support for {@link #inBitmap} was
99          * introduced in {@link android.os.Build.VERSION_CODES#JELLY_BEAN}. All
100          * formats supported by BitmapRegionDecoder support Bitmap reuse via
101          * {@link #inBitmap}.</p>
102          *
103          * @see Bitmap#reconfigure(int,int, android.graphics.Bitmap.Config)
104          */
105         public Bitmap inBitmap;
106 
107         /**
108          * If set, decode methods will always return a mutable Bitmap instead of
109          * an immutable one. This can be used for instance to programmatically apply
110          * effects to a Bitmap loaded through BitmapFactory.
111          * <p>Can not be set simultaneously with inPreferredConfig =
112          * {@link android.graphics.Bitmap.Config#HARDWARE},
113          * because hardware bitmaps are always immutable.
114          */
115         @SuppressWarnings({"UnusedDeclaration"}) // used in native code
116         public boolean inMutable;
117 
118         /**
119          * If set to true, the decoder will return null (no bitmap), but
120          * the <code>out...</code> fields will still be set, allowing the caller to
121          * query the bitmap without having to allocate the memory for its pixels.
122          */
123         public boolean inJustDecodeBounds;
124 
125         /**
126          * If set to a value > 1, requests the decoder to subsample the original
127          * image, returning a smaller image to save memory. The sample size is
128          * the number of pixels in either dimension that correspond to a single
129          * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
130          * an image that is 1/4 the width/height of the original, and 1/16 the
131          * number of pixels. Any value <= 1 is treated the same as 1. Note: the
132          * decoder uses a final value based on powers of 2, any other value will
133          * be rounded down to the nearest power of 2.
134          */
135         public int inSampleSize;
136 
137         /**
138          * If this is non-null, the decoder will try to decode into this
139          * internal configuration. If it is null, or the request cannot be met,
140          * the decoder will try to pick the best matching config based on the
141          * system's screen depth, and characteristics of the original image such
142          * as if it has per-pixel alpha (requiring a config that also does).
143          *
144          * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by
145          * default.
146          */
147         public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
148 
149         /**
150          * <p>If this is non-null, the decoder will try to decode into this
151          * color space. If it is null, or the request cannot be met,
152          * the decoder will pick either the color space embedded in the image
153          * or the color space best suited for the requested image configuration
154          * (for instance {@link ColorSpace.Named#SRGB sRGB} for
155          * {@link Bitmap.Config#ARGB_8888} configuration and
156          * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for
157          * {@link Bitmap.Config#RGBA_F16}).</p>
158          *
159          * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
160          * currently supported. An <code>IllegalArgumentException</code> will
161          * be thrown by the decode methods when setting a non-RGB color space
162          * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
163          *
164          * <p class="note">
165          * Prior to {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
166          * the specified color space's transfer function must be
167          * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
168          * <code>IllegalArgumentException</code> will be thrown by the decode methods
169          * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
170          * specified color space returns null.
171          *
172          * Starting from {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
173          * non ICC parametric curve transfer function is allowed.
174          * E.g., {@link ColorSpace.Named#BT2020_HLG BT2020_HLG}.</p>
175          *
176          * <p>After decode, the bitmap's color space is stored in
177          * {@link #outColorSpace}.</p>
178          */
179         public ColorSpace inPreferredColorSpace = null;
180 
181         /**
182          * If true (which is the default), the resulting bitmap will have its
183          * color channels pre-multipled by the alpha channel.
184          *
185          * <p>This should NOT be set to false for images to be directly drawn by
186          * the view system or through a {@link Canvas}. The view system and
187          * {@link Canvas} assume all drawn images are pre-multiplied to simplify
188          * draw-time blending, and will throw a RuntimeException when
189          * un-premultiplied are drawn.</p>
190          *
191          * <p>This is likely only useful if you want to manipulate raw encoded
192          * image data, e.g. with RenderScript or custom OpenGL.</p>
193          *
194          * <p>This does not affect bitmaps without an alpha channel.</p>
195          *
196          * <p>Setting this flag to false while setting {@link #inScaled} to true
197          * may result in incorrect colors.</p>
198          *
199          * @see Bitmap#hasAlpha()
200          * @see Bitmap#isPremultiplied()
201          * @see #inScaled
202          */
203         public boolean inPremultiplied;
204 
205         /**
206          * @deprecated As of {@link android.os.Build.VERSION_CODES#N}, this is
207          * ignored.
208          *
209          * In {@link android.os.Build.VERSION_CODES#M} and below, if dither is
210          * true, the decoder will attempt to dither the decoded image.
211          */
212         public boolean inDither;
213 
214         /**
215          * The pixel density to use for the bitmap.  This will always result
216          * in the returned bitmap having a density set for it (see
217          * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)}).  In addition,
218          * if {@link #inScaled} is set (which it is by default} and this
219          * density does not match {@link #inTargetDensity}, then the bitmap
220          * will be scaled to the target density before being returned.
221          *
222          * <p>If this is 0,
223          * {@link BitmapFactory#decodeResource(Resources, int)},
224          * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
225          * and {@link BitmapFactory#decodeResourceStream}
226          * will fill in the density associated with the resource.  The other
227          * functions will leave it as-is and no density will be applied.
228          *
229          * @see #inTargetDensity
230          * @see #inScreenDensity
231          * @see #inScaled
232          * @see Bitmap#setDensity(int)
233          * @see android.util.DisplayMetrics#densityDpi
234          */
235         public int inDensity;
236 
237         /**
238          * The pixel density of the destination this bitmap will be drawn to.
239          * This is used in conjunction with {@link #inDensity} and
240          * {@link #inScaled} to determine if and how to scale the bitmap before
241          * returning it.
242          *
243          * <p>If this is 0,
244          * {@link BitmapFactory#decodeResource(Resources, int)},
245          * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
246          * and {@link BitmapFactory#decodeResourceStream}
247          * will fill in the density associated the Resources object's
248          * DisplayMetrics.  The other
249          * functions will leave it as-is and no scaling for density will be
250          * performed.
251          *
252          * @see #inDensity
253          * @see #inScreenDensity
254          * @see #inScaled
255          * @see android.util.DisplayMetrics#densityDpi
256          */
257         public int inTargetDensity;
258 
259         /**
260          * The pixel density of the actual screen that is being used.  This is
261          * purely for applications running in density compatibility code, where
262          * {@link #inTargetDensity} is actually the density the application
263          * sees rather than the real screen density.
264          *
265          * <p>By setting this, you
266          * allow the loading code to avoid scaling a bitmap that is currently
267          * in the screen density up/down to the compatibility density.  Instead,
268          * if {@link #inDensity} is the same as {@link #inScreenDensity}, the
269          * bitmap will be left as-is.  Anything using the resulting bitmap
270          * must also used {@link Bitmap#getScaledWidth(int)
271          * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
272          * Bitmap.getScaledHeight} to account for any different between the
273          * bitmap's density and the target's density.
274          *
275          * <p>This is never set automatically for the caller by
276          * {@link BitmapFactory} itself.  It must be explicitly set, since the
277          * caller must deal with the resulting bitmap in a density-aware way.
278          *
279          * @see #inDensity
280          * @see #inTargetDensity
281          * @see #inScaled
282          * @see android.util.DisplayMetrics#densityDpi
283          */
284         public int inScreenDensity;
285 
286         /**
287          * When this flag is set, if {@link #inDensity} and
288          * {@link #inTargetDensity} are not 0, the
289          * bitmap will be scaled to match {@link #inTargetDensity} when loaded,
290          * rather than relying on the graphics system scaling it each time it
291          * is drawn to a Canvas.
292          *
293          * <p>BitmapRegionDecoder ignores this flag, and will not scale output
294          * based on density. (though {@link #inSampleSize} is supported)</p>
295          *
296          * <p>This flag is turned on by default and should be turned off if you need
297          * a non-scaled version of the bitmap.  Nine-patch bitmaps ignore this
298          * flag and are always scaled.
299          *
300          * <p>If {@link #inPremultiplied} is set to false, and the image has alpha,
301          * setting this flag to true may result in incorrect colors.
302          */
303         public boolean inScaled;
304 
305         /**
306          * @deprecated As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this is
307          * ignored.
308          *
309          * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, if this
310          * is set to true, then the resulting bitmap will allocate its
311          * pixels such that they can be purged if the system needs to reclaim
312          * memory. In that instance, when the pixels need to be accessed again
313          * (e.g. the bitmap is drawn, getPixels() is called), they will be
314          * automatically re-decoded.
315          *
316          * <p>For the re-decode to happen, the bitmap must have access to the
317          * encoded data, either by sharing a reference to the input
318          * or by making a copy of it. This distinction is controlled by
319          * inInputShareable. If this is true, then the bitmap may keep a shallow
320          * reference to the input. If this is false, then the bitmap will
321          * explicitly make a copy of the input data, and keep that. Even if
322          * sharing is allowed, the implementation may still decide to make a
323          * deep copy of the input data.</p>
324          *
325          * <p>While inPurgeable can help avoid big Dalvik heap allocations (from
326          * API level 11 onward), it sacrifices performance predictability since any
327          * image that the view system tries to draw may incur a decode delay which
328          * can lead to dropped frames. Therefore, most apps should avoid using
329          * inPurgeable to allow for a fast and fluid UI. To minimize Dalvik heap
330          * allocations use the {@link #inBitmap} flag instead.</p>
331          *
332          * <p class="note"><strong>Note:</strong> This flag is ignored when used
333          * with {@link #decodeResource(Resources, int,
334          * android.graphics.BitmapFactory.Options)} or {@link #decodeFile(String,
335          * android.graphics.BitmapFactory.Options)}.</p>
336          */
337         @Deprecated
338         public boolean inPurgeable;
339 
340         /**
341          * @deprecated As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this is
342          * ignored.
343          *
344          * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, this
345          * field works in conjuction with inPurgeable. If inPurgeable is false,
346          * then this field is ignored. If inPurgeable is true, then this field
347          * determines whether the bitmap can share a reference to the input
348          * data (inputstream, array, etc.) or if it must make a deep copy.
349          */
350         @Deprecated
351         public boolean inInputShareable;
352 
353         /**
354          * @deprecated As of {@link android.os.Build.VERSION_CODES#N}, this is
355          * ignored.  The output will always be high quality.
356          *
357          * In {@link android.os.Build.VERSION_CODES#M} and below, if
358          * inPreferQualityOverSpeed is set to true, the decoder will try to
359          * decode the reconstructed image to a higher quality even at the
360          * expense of the decoding speed. Currently the field only affects JPEG
361          * decode, in the case of which a more accurate, but slightly slower,
362          * IDCT method will be used instead.
363          */
364         @Deprecated
365         public boolean inPreferQualityOverSpeed;
366 
367         /**
368          * The resulting width of the bitmap. If {@link #inJustDecodeBounds} is
369          * set to false, this will be width of the output bitmap after any
370          * scaling is applied. If true, it will be the width of the input image
371          * without any accounting for scaling.
372          *
373          * <p>outWidth will be set to -1 if there is an error trying to decode.</p>
374          */
375         public int outWidth;
376 
377         /**
378          * The resulting height of the bitmap. If {@link #inJustDecodeBounds} is
379          * set to false, this will be height of the output bitmap after any
380          * scaling is applied. If true, it will be the height of the input image
381          * without any accounting for scaling.
382          *
383          * <p>outHeight will be set to -1 if there is an error trying to decode.</p>
384          */
385         public int outHeight;
386 
387         /**
388          * If known, this string is set to the mimetype of the decoded image.
389          * If not known, or there is an error, it is set to null.
390          */
391         public String outMimeType;
392 
393         /**
394          * If known, the config the decoded bitmap will have.
395          * If not known, or there is an error, it is set to null.
396          */
397         public Bitmap.Config outConfig;
398 
399         /**
400          * If known, the color space the decoded bitmap will have. Note that the
401          * output color space is not guaranteed to be the color space the bitmap
402          * is encoded with. If not known (when the config is
403          * {@link Bitmap.Config#ALPHA_8} for instance), or there is an error,
404          * it is set to null.
405          */
406         public ColorSpace outColorSpace;
407 
408         /**
409          * Temp storage to use for decoding.  Suggest 16K or so.
410          */
411         public byte[] inTempStorage;
412 
413         /**
414          * @deprecated As of {@link android.os.Build.VERSION_CODES#N}, see
415          * comments on {@link #requestCancelDecode()}.
416          *
417          * Flag to indicate that cancel has been called on this object.  This
418          * is useful if there's an intermediary that wants to first decode the
419          * bounds and then decode the image.  In that case the intermediary
420          * can check, inbetween the bounds decode and the image decode, to see
421          * if the operation is canceled.
422          */
423         @Deprecated
424         public boolean mCancel;
425 
426         /**
427          *  @deprecated As of {@link android.os.Build.VERSION_CODES#N}, this
428          *  will not affect the decode, though it will still set mCancel.
429          *
430          *  In {@link android.os.Build.VERSION_CODES#M} and below, if this can
431          *  be called from another thread while this options object is inside
432          *  a decode... call. Calling this will notify the decoder that it
433          *  should cancel its operation. This is not guaranteed to cancel the
434          *  decode, but if it does, the decoder... operation will return null,
435          *  or if inJustDecodeBounds is true, will set outWidth/outHeight
436          *  to -1
437          */
438         @Deprecated
requestCancelDecode()439         public void requestCancelDecode() {
440             mCancel = true;
441         }
442 
validate(Options opts)443         static void validate(Options opts) {
444             if (opts == null) return;
445 
446             if (opts.inBitmap != null) {
447                 if (opts.inBitmap.getConfig() == Bitmap.Config.HARDWARE) {
448                     throw new IllegalArgumentException(
449                             "Bitmaps with Config.HARDWARE are always immutable");
450                 }
451                 if (opts.inBitmap.isRecycled()) {
452                     throw new IllegalArgumentException(
453                             "Cannot reuse a recycled Bitmap");
454                 }
455             }
456 
457             if (opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
458                 throw new IllegalArgumentException("Bitmaps with Config.HARDWARE cannot be " +
459                         "decoded into - they are immutable");
460             }
461 
462             if (opts.inPreferredColorSpace != null) {
463                 if (!(opts.inPreferredColorSpace instanceof ColorSpace.Rgb)) {
464                     throw new IllegalArgumentException("The destination color space must use the " +
465                             "RGB color model");
466                 }
467                 if (!opts.inPreferredColorSpace.equals(ColorSpace.get(ColorSpace.Named.BT2020_HLG))
468                         && !opts.inPreferredColorSpace.equals(
469                             ColorSpace.get(ColorSpace.Named.BT2020_PQ))
470                         && ((ColorSpace.Rgb) opts.inPreferredColorSpace)
471                             .getTransferParameters() == null) {
472                     throw new IllegalArgumentException("The destination color space must use an " +
473                             "ICC parametric transfer function");
474                 }
475             }
476         }
477 
478         /**
479          *  Helper for passing inBitmap's native pointer to native.
480          */
nativeInBitmap(Options opts)481         static long nativeInBitmap(Options opts) {
482             if (opts == null || opts.inBitmap == null) {
483                 return 0;
484             }
485             // Clear out the gainmap since we don't attempt to reuse it and don't want to
486             // accidentally keep it on the re-used bitmap
487             opts.inBitmap.setGainmap(null);
488             return opts.inBitmap.getNativeInstance();
489         }
490 
491         /**
492          *  Helper for passing SkColorSpace pointer to native.
493          *
494          *  @throws IllegalArgumentException if the ColorSpace is not Rgb or does
495          *          not have TransferParameters.
496          */
nativeColorSpace(Options opts)497         static long nativeColorSpace(Options opts) {
498             if (opts == null || opts.inPreferredColorSpace == null) {
499                 return 0;
500             }
501 
502             return opts.inPreferredColorSpace.getNativeInstance();
503         }
504 
505     }
506 
507     /**
508      * Decode a file path into a bitmap. If the specified file name is null,
509      * or cannot be decoded into a bitmap, the function returns null.
510      *
511      * @param pathName complete path name for the file to be decoded.
512      * @param opts null-ok; Options that control downsampling and whether the
513      *             image should be completely decoded, or just is size returned.
514      * @return The decoded bitmap, or null if the image data could not be
515      *         decoded, or, if opts is non-null, if opts requested only the
516      *         size be returned (in opts.outWidth and opts.outHeight)
517      * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
518      *         is {@link android.graphics.Bitmap.Config#HARDWARE}
519      *         and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
520      *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
521      *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
522      */
decodeFile(String pathName, Options opts)523     public static Bitmap decodeFile(String pathName, Options opts) {
524         validate(opts);
525         Bitmap bm = null;
526         InputStream stream = null;
527         try {
528             stream = new FileInputStream(pathName);
529             bm = decodeStream(stream, null, opts);
530         } catch (Exception e) {
531             /*  do nothing.
532                 If the exception happened on open, bm will be null.
533             */
534             Log.e("BitmapFactory", "Unable to decode stream: " + e);
535         } finally {
536             if (stream != null) {
537                 try {
538                     stream.close();
539                 } catch (IOException e) {
540                     // do nothing here
541                 }
542             }
543         }
544         return bm;
545     }
546 
547     /**
548      * Decode a file path into a bitmap. If the specified file name is null,
549      * or cannot be decoded into a bitmap, the function returns null.
550      *
551      * @param pathName complete path name for the file to be decoded.
552      * @return the resulting decoded bitmap, or null if it could not be decoded.
553      */
decodeFile(String pathName)554     public static Bitmap decodeFile(String pathName) {
555         return decodeFile(pathName, null);
556     }
557 
558     /**
559      * Decode a new Bitmap from an InputStream. This InputStream was obtained from
560      * resources, which we pass to be able to scale the bitmap accordingly.
561      * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
562      *         is {@link android.graphics.Bitmap.Config#HARDWARE}
563      *         and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
564      *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
565      *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
566      */
567     @Nullable
decodeResourceStream(@ullable Resources res, @Nullable TypedValue value, @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts)568     public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
569             @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
570         validate(opts);
571         if (opts == null) {
572             opts = new Options();
573         }
574 
575         if (opts.inDensity == 0 && value != null) {
576             final int density = value.density;
577             if (density == TypedValue.DENSITY_DEFAULT) {
578                 opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
579             } else if (density != TypedValue.DENSITY_NONE) {
580                 opts.inDensity = density;
581             }
582         }
583 
584         if (opts.inTargetDensity == 0 && res != null) {
585             opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
586         }
587 
588         return decodeStream(is, pad, opts);
589     }
590 
591     /**
592      * Synonym for opening the given resource and calling
593      * {@link #decodeResourceStream}.
594      *
595      * @param res   The resources object containing the image data
596      * @param id The resource id of the image data
597      * @param opts null-ok; Options that control downsampling and whether the
598      *             image should be completely decoded, or just is size returned.
599      * @return The decoded bitmap, or null if the image data could not be
600      *         decoded, or, if opts is non-null, if opts requested only the
601      *         size be returned (in opts.outWidth and opts.outHeight)
602      * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
603      *         is {@link android.graphics.Bitmap.Config#HARDWARE}
604      *         and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
605      *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
606      *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
607      */
decodeResource(Resources res, int id, Options opts)608     public static Bitmap decodeResource(Resources res, int id, Options opts) {
609         validate(opts);
610         Bitmap bm = null;
611         InputStream is = null;
612 
613         try {
614             final TypedValue value = new TypedValue();
615             is = res.openRawResource(id, value);
616 
617             bm = decodeResourceStream(res, value, is, null, opts);
618         } catch (Exception e) {
619             /*  do nothing.
620                 If the exception happened on open, bm will be null.
621                 If it happened on close, bm is still valid.
622             */
623         } finally {
624             try {
625                 if (is != null) is.close();
626             } catch (IOException e) {
627                 // Ignore
628             }
629         }
630 
631         if (bm == null && opts != null && opts.inBitmap != null) {
632             throw new IllegalArgumentException("Problem decoding into existing bitmap");
633         }
634 
635         return bm;
636     }
637 
638     /**
639      * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}
640      * with null Options.
641      *
642      * @param res The resources object containing the image data
643      * @param id The resource id of the image data
644      * @return The decoded bitmap, or null if the image could not be decoded.
645      */
decodeResource(Resources res, int id)646     public static Bitmap decodeResource(Resources res, int id) {
647         return decodeResource(res, id, null);
648     }
649 
650     /**
651      * Decode an immutable bitmap from the specified byte array.
652      *
653      * @param data byte array of compressed image data
654      * @param offset offset into imageData for where the decoder should begin
655      *               parsing.
656      * @param length the number of bytes, beginning at offset, to parse
657      * @param opts null-ok; Options that control downsampling and whether the
658      *             image should be completely decoded, or just is size returned.
659      * @return The decoded bitmap, or null if the image data could not be
660      *         decoded, or, if opts is non-null, if opts requested only the
661      *         size be returned (in opts.outWidth and opts.outHeight)
662      * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
663      *         is {@link android.graphics.Bitmap.Config#HARDWARE}
664      *         and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
665      *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
666      *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
667      */
decodeByteArray(byte[] data, int offset, int length, Options opts)668     public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
669         if ((offset | length) < 0 || data.length < offset + length) {
670             throw new ArrayIndexOutOfBoundsException();
671         }
672         validate(opts);
673 
674         Bitmap bm;
675 
676         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
677         try {
678             bm = nativeDecodeByteArray(data, offset, length, opts,
679                     Options.nativeInBitmap(opts),
680                     Options.nativeColorSpace(opts));
681 
682             if (bm == null && opts != null && opts.inBitmap != null) {
683                 throw new IllegalArgumentException("Problem decoding into existing bitmap");
684             }
685             setDensityFromOptions(bm, opts);
686         } finally {
687             Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
688         }
689 
690         return bm;
691     }
692 
693     /**
694      * Decode an immutable bitmap from the specified byte array.
695      *
696      * @param data byte array of compressed image data
697      * @param offset offset into imageData for where the decoder should begin
698      *               parsing.
699      * @param length the number of bytes, beginning at offset, to parse
700      * @return The decoded bitmap, or null if the image could not be decoded.
701      */
decodeByteArray(byte[] data, int offset, int length)702     public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
703         return decodeByteArray(data, offset, length, null);
704     }
705 
706     /**
707      * Set the newly decoded bitmap's density based on the Options.
708      */
setDensityFromOptions(Bitmap outputBitmap, Options opts)709     private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
710         if (outputBitmap == null || opts == null) return;
711 
712         final int density = opts.inDensity;
713         if (density != 0) {
714             outputBitmap.setDensity(density);
715             final int targetDensity = opts.inTargetDensity;
716             if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
717                 return;
718             }
719 
720             byte[] np = outputBitmap.getNinePatchChunk();
721             final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
722             if (opts.inScaled || isNinePatch) {
723                 outputBitmap.setDensity(targetDensity);
724             }
725         } else if (opts.inBitmap != null) {
726             // bitmap was reused, ensure density is reset
727             outputBitmap.setDensity(Bitmap.getDefaultDensity());
728         }
729     }
730 
731     /**
732      * Decode an input stream into a bitmap. If the input stream is null, or
733      * cannot be used to decode a bitmap, the function returns null.
734      * The stream's position will be where ever it was after the encoded data
735      * was read.
736      *
737      * @param is The input stream that holds the raw data to be decoded into a
738      *           bitmap.
739      * @param outPadding If not null, return the padding rect for the bitmap if
740      *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
741      *                   no bitmap is returned (null) then padding is
742      *                   unchanged.
743      * @param opts null-ok; Options that control downsampling and whether the
744      *             image should be completely decoded, or just is size returned.
745      * @return The decoded bitmap, or null if the image data could not be
746      *         decoded, or, if opts is non-null, if opts requested only the
747      *         size be returned (in opts.outWidth and opts.outHeight)
748      * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
749      *         is {@link android.graphics.Bitmap.Config#HARDWARE}
750      *         and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
751      *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
752      *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
753      *
754      * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
755      * if {@link InputStream#markSupported is.markSupported()} returns true,
756      * <code>is.mark(1024)</code> would be called. As of
757      * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
758      */
759     @Nullable
decodeStream(@ullable InputStream is, @Nullable Rect outPadding, @Nullable Options opts)760     public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding,
761             @Nullable Options opts) {
762         // we don't throw in this case, thus allowing the caller to only check
763         // the cache, and not force the image to be decoded.
764         if (is == null) {
765             return null;
766         }
767         validate(opts);
768 
769         Bitmap bm = null;
770 
771         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
772         try {
773             if (is instanceof AssetManager.AssetInputStream) {
774                 final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
775                 bm = nativeDecodeAsset(asset, outPadding, opts, Options.nativeInBitmap(opts),
776                     Options.nativeColorSpace(opts));
777             } else {
778                 bm = decodeStreamInternal(is, outPadding, opts);
779             }
780 
781             if (bm == null && opts != null && opts.inBitmap != null) {
782                 throw new IllegalArgumentException("Problem decoding into existing bitmap");
783             }
784 
785             setDensityFromOptions(bm, opts);
786         } finally {
787             Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
788         }
789 
790         return bm;
791     }
792 
793     /**
794      * Private helper function for decoding an InputStream natively. Buffers the input enough to
795      * do a rewind as needed, and supplies temporary storage if necessary. is MUST NOT be null.
796      */
decodeStreamInternal(@onNull InputStream is, @Nullable Rect outPadding, @Nullable Options opts)797     private static Bitmap decodeStreamInternal(@NonNull InputStream is,
798             @Nullable Rect outPadding, @Nullable Options opts) {
799         // ASSERT(is != null);
800         byte [] tempStorage = null;
801         if (opts != null) tempStorage = opts.inTempStorage;
802         if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
803         return nativeDecodeStream(is, tempStorage, outPadding, opts,
804                 Options.nativeInBitmap(opts),
805                 Options.nativeColorSpace(opts));
806     }
807 
808     /**
809      * Decode an input stream into a bitmap. If the input stream is null, or
810      * cannot be used to decode a bitmap, the function returns null.
811      * The stream's position will be where ever it was after the encoded data
812      * was read.
813      *
814      * @param is The input stream that holds the raw data to be decoded into a
815      *           bitmap.
816      * @return The decoded bitmap, or null if the image data could not be decoded.
817      */
decodeStream(InputStream is)818     public static Bitmap decodeStream(InputStream is) {
819         return decodeStream(is, null, null);
820     }
821 
822     /**
823      * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
824      * return null. The position within the descriptor will not be changed when
825      * this returns, so the descriptor can be used again as-is.
826      *
827      * @param fd The file descriptor containing the bitmap data to decode
828      * @param outPadding If not null, return the padding rect for the bitmap if
829      *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
830      *                   no bitmap is returned (null) then padding is
831      *                   unchanged.
832      * @param opts null-ok; Options that control downsampling and whether the
833      *             image should be completely decoded, or just its size returned.
834      * @return the decoded bitmap, or null
835      * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
836      *         is {@link android.graphics.Bitmap.Config#HARDWARE}
837      *         and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
838      *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
839      *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
840      */
decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)841     public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
842         validate(opts);
843         Bitmap bm;
844 
845         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor");
846         try {
847             if (nativeIsSeekable(fd)) {
848                 bm = nativeDecodeFileDescriptor(fd, outPadding, opts,
849                         Options.nativeInBitmap(opts),
850                         Options.nativeColorSpace(opts));
851             } else {
852                 FileInputStream fis = new FileInputStream(fd);
853                 try {
854                     bm = decodeStreamInternal(fis, outPadding, opts);
855                 } finally {
856                     try {
857                         fis.close();
858                     } catch (Throwable t) {/* ignore */}
859                 }
860             }
861 
862             if (bm == null && opts != null && opts.inBitmap != null) {
863                 throw new IllegalArgumentException("Problem decoding into existing bitmap");
864             }
865 
866             setDensityFromOptions(bm, opts);
867         } finally {
868             Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
869         }
870         return bm;
871     }
872 
873     /**
874      * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
875      * return null. The position within the descriptor will not be changed when
876      * this returns, so the descriptor can be used again as is.
877      *
878      * @param fd The file descriptor containing the bitmap data to decode
879      * @return the decoded bitmap, or null
880      */
decodeFileDescriptor(FileDescriptor fd)881     public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
882         return decodeFileDescriptor(fd, null, null);
883     }
884 
885     @UnsupportedAppUsage
nativeDecodeStream(InputStream is, byte[] storage, Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle)886     private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
887             Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle);
888     @UnsupportedAppUsage
nativeDecodeFileDescriptor(FileDescriptor fd, Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle)889     private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
890             Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle);
891     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
nativeDecodeAsset(long nativeAsset, Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle)892     private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts,
893             long inBitmapHandle, long colorSpaceHandle);
894     @UnsupportedAppUsage
nativeDecodeByteArray(byte[] data, int offset, int length, Options opts, long inBitmapHandle, long colorSpaceHandle)895     private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
896             int length, Options opts, long inBitmapHandle, long colorSpaceHandle);
nativeIsSeekable(FileDescriptor fd)897     private static native boolean nativeIsSeekable(FileDescriptor fd);
898 }
899