BitmapFactory.java revision f7142e3e8bfba982ad73a7c7a6a992491b7cfb43
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 17package android.graphics; 18 19import android.content.res.AssetManager; 20import android.content.res.Resources; 21import android.os.Trace; 22import android.util.DisplayMetrics; 23import android.util.Log; 24import android.util.TypedValue; 25 26import java.io.FileDescriptor; 27import java.io.FileInputStream; 28import java.io.IOException; 29import java.io.InputStream; 30 31/** 32 * Creates Bitmap objects from various sources, including files, streams, 33 * and byte-arrays. 34 */ 35public class BitmapFactory { 36 private static final int DECODE_BUFFER_SIZE = 16 * 1024; 37 38 public static class Options { 39 /** 40 * Create a default Options object, which if left unchanged will give 41 * the same result from the decoder as if null were passed. 42 */ 43 public Options() { 44 inDither = false; 45 inScaled = true; 46 inPremultiplied = true; 47 } 48 49 /** 50 * If set, decode methods that take the Options object will attempt to 51 * reuse this bitmap when loading content. If the decode operation 52 * cannot use this bitmap, the decode method will return 53 * <code>null</code> and will throw an IllegalArgumentException. The 54 * current implementation necessitates that the reused bitmap be 55 * mutable, and the resulting reused bitmap will continue to remain 56 * mutable even when decoding a resource which would normally result in 57 * an immutable bitmap.</p> 58 * 59 * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, any 60 * mutable bitmap can be reused to decode any other bitmaps as long as 61 * the resulting {@link Bitmap#getByteCount() byte count} of the decoded 62 * bitmap is less than or equal to the {@link 63 * Bitmap#getAllocationByteCount() allocated byte count} of the reused 64 * bitmap. This can be because the intrinsic size is smaller, or its 65 * size post scaling (for density / sample size) is smaller.</p> 66 * 67 * <p>Prior to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE} 68 * additional constraints apply: The image being decoded (whether as a 69 * resource or as a stream) must be in jpeg or png format. Only equal 70 * sized bitmaps are supported, with {@link #inSampleSize} set to 1. 71 * Additionally, the {@link android.graphics.Bitmap.Config 72 * configuration} of the reused bitmap will override the setting of 73 * {@link #inPreferredConfig}, if set.</p> 74 * 75 * <p>You should still always use the returned Bitmap of the decode 76 * method and not assume that reusing the bitmap worked, due to the 77 * constraints outlined above and failure situations that can occur. 78 * Checking whether the return value matches the value of the inBitmap 79 * set in the Options structure will indicate if the bitmap was reused, 80 * but in all cases you should use the Bitmap returned by the decoding 81 * function to ensure that you are using the bitmap that was used as the 82 * decode destination.</p> 83 * 84 * @see Bitmap#reconfigure(int,int,Config) 85 */ 86 public Bitmap inBitmap; 87 88 /** 89 * If set, decode methods will always return a mutable Bitmap instead of 90 * an immutable one. This can be used for instance to programmatically apply 91 * effects to a Bitmap loaded through BitmapFactory. 92 */ 93 @SuppressWarnings({"UnusedDeclaration"}) // used in native code 94 public boolean inMutable; 95 96 /** 97 * If set to true, the decoder will return null (no bitmap), but 98 * the out... fields will still be set, allowing the caller to query 99 * the bitmap without having to allocate the memory for its pixels. 100 */ 101 public boolean inJustDecodeBounds; 102 103 /** 104 * If set to a value > 1, requests the decoder to subsample the original 105 * image, returning a smaller image to save memory. The sample size is 106 * the number of pixels in either dimension that correspond to a single 107 * pixel in the decoded bitmap. For example, inSampleSize == 4 returns 108 * an image that is 1/4 the width/height of the original, and 1/16 the 109 * number of pixels. Any value <= 1 is treated the same as 1. Note: the 110 * decoder uses a final value based on powers of 2, any other value will 111 * be rounded down to the nearest power of 2. 112 */ 113 public int inSampleSize; 114 115 /** 116 * If this is non-null, the decoder will try to decode into this 117 * internal configuration. If it is null, or the request cannot be met, 118 * the decoder will try to pick the best matching config based on the 119 * system's screen depth, and characteristics of the original image such 120 * as if it has per-pixel alpha (requiring a config that also does). 121 * 122 * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by 123 * default. 124 */ 125 public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888; 126 127 /** 128 * If true (which is the default), the resulting bitmap will have its 129 * color channels pre-multipled by the alpha channel. 130 * 131 * <p>This should NOT be set to false for images to be directly drawn by 132 * the view system or through a {@link Canvas}. The view system and 133 * {@link Canvas} assume all drawn images are pre-multiplied to simplify 134 * draw-time blending, and will throw a RuntimeException when 135 * un-premultiplied are drawn.</p> 136 * 137 * <p>This is likely only useful if you want to manipulate raw encoded 138 * image data, e.g. with RenderScript or custom OpenGL.</p> 139 * 140 * <p>This does not affect bitmaps without an alpha channel.</p> 141 * 142 * @see Bitmap#hasAlpha() 143 * @see Bitmap#isPremultiplied() 144 */ 145 public boolean inPremultiplied; 146 147 /** 148 * If dither is true, the decoder will attempt to dither the decoded 149 * image. 150 */ 151 public boolean inDither; 152 153 /** 154 * The pixel density to use for the bitmap. This will always result 155 * in the returned bitmap having a density set for it (see 156 * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)}). In addition, 157 * if {@link #inScaled} is set (which it is by default} and this 158 * density does not match {@link #inTargetDensity}, then the bitmap 159 * will be scaled to the target density before being returned. 160 * 161 * <p>If this is 0, 162 * {@link BitmapFactory#decodeResource(Resources, int)}, 163 * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}, 164 * and {@link BitmapFactory#decodeResourceStream} 165 * will fill in the density associated with the resource. The other 166 * functions will leave it as-is and no density will be applied. 167 * 168 * @see #inTargetDensity 169 * @see #inScreenDensity 170 * @see #inScaled 171 * @see Bitmap#setDensity(int) 172 * @see android.util.DisplayMetrics#densityDpi 173 */ 174 public int inDensity; 175 176 /** 177 * The pixel density of the destination this bitmap will be drawn to. 178 * This is used in conjunction with {@link #inDensity} and 179 * {@link #inScaled} to determine if and how to scale the bitmap before 180 * returning it. 181 * 182 * <p>If this is 0, 183 * {@link BitmapFactory#decodeResource(Resources, int)}, 184 * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}, 185 * and {@link BitmapFactory#decodeResourceStream} 186 * will fill in the density associated the Resources object's 187 * DisplayMetrics. The other 188 * functions will leave it as-is and no scaling for density will be 189 * performed. 190 * 191 * @see #inDensity 192 * @see #inScreenDensity 193 * @see #inScaled 194 * @see android.util.DisplayMetrics#densityDpi 195 */ 196 public int inTargetDensity; 197 198 /** 199 * The pixel density of the actual screen that is being used. This is 200 * purely for applications running in density compatibility code, where 201 * {@link #inTargetDensity} is actually the density the application 202 * sees rather than the real screen density. 203 * 204 * <p>By setting this, you 205 * allow the loading code to avoid scaling a bitmap that is currently 206 * in the screen density up/down to the compatibility density. Instead, 207 * if {@link #inDensity} is the same as {@link #inScreenDensity}, the 208 * bitmap will be left as-is. Anything using the resulting bitmap 209 * must also used {@link Bitmap#getScaledWidth(int) 210 * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight 211 * Bitmap.getScaledHeight} to account for any different between the 212 * bitmap's density and the target's density. 213 * 214 * <p>This is never set automatically for the caller by 215 * {@link BitmapFactory} itself. It must be explicitly set, since the 216 * caller must deal with the resulting bitmap in a density-aware way. 217 * 218 * @see #inDensity 219 * @see #inTargetDensity 220 * @see #inScaled 221 * @see android.util.DisplayMetrics#densityDpi 222 */ 223 public int inScreenDensity; 224 225 /** 226 * When this flag is set, if {@link #inDensity} and 227 * {@link #inTargetDensity} are not 0, the 228 * bitmap will be scaled to match {@link #inTargetDensity} when loaded, 229 * rather than relying on the graphics system scaling it each time it 230 * is drawn to a Canvas. 231 * 232 * <p>This flag is turned on by default and should be turned off if you need 233 * a non-scaled version of the bitmap. Nine-patch bitmaps ignore this 234 * flag and are always scaled. 235 */ 236 public boolean inScaled; 237 238 /** 239 * If this is set to true, then the resulting bitmap will allocate its 240 * pixels such that they can be purged if the system needs to reclaim 241 * memory. In that instance, when the pixels need to be accessed again 242 * (e.g. the bitmap is drawn, getPixels() is called), they will be 243 * automatically re-decoded. 244 * 245 * For the re-decode to happen, the bitmap must have access to the 246 * encoded data, either by sharing a reference to the input 247 * or by making a copy of it. This distinction is controlled by 248 * inInputShareable. If this is true, then the bitmap may keep a shallow 249 * reference to the input. If this is false, then the bitmap will 250 * explicitly make a copy of the input data, and keep that. Even if 251 * sharing is allowed, the implementation may still decide to make a 252 * deep copy of the input data. 253 */ 254 public boolean inPurgeable; 255 256 /** 257 * This field works in conjuction with inPurgeable. If inPurgeable is 258 * false, then this field is ignored. If inPurgeable is true, then this 259 * field determines whether the bitmap can share a reference to the 260 * input data (inputstream, array, etc.) or if it must make a deep copy. 261 */ 262 public boolean inInputShareable; 263 264 /** 265 * If inPreferQualityOverSpeed is set to true, the decoder will try to 266 * decode the reconstructed image to a higher quality even at the 267 * expense of the decoding speed. Currently the field only affects JPEG 268 * decode, in the case of which a more accurate, but slightly slower, 269 * IDCT method will be used instead. 270 */ 271 public boolean inPreferQualityOverSpeed; 272 273 /** 274 * The resulting width of the bitmap. If {@link #inJustDecodeBounds} is 275 * set to false, this will be width of the output bitmap after any 276 * scaling is applied. If true, it will be the width of the input image 277 * without any accounting for scaling. 278 * 279 * <p>outWidth will be set to -1 if there is an error trying to decode.</p> 280 */ 281 public int outWidth; 282 283 /** 284 * The resulting height of the bitmap. If {@link #inJustDecodeBounds} is 285 * set to false, this will be height of the output bitmap after any 286 * scaling is applied. If true, it will be the height of the input image 287 * without any accounting for scaling. 288 * 289 * <p>outHeight will be set to -1 if there is an error trying to decode.</p> 290 */ 291 public int outHeight; 292 293 /** 294 * If known, this string is set to the mimetype of the decoded image. 295 * If not know, or there is an error, it is set to null. 296 */ 297 public String outMimeType; 298 299 /** 300 * Temp storage to use for decoding. Suggest 16K or so. 301 */ 302 public byte[] inTempStorage; 303 304 private native void requestCancel(); 305 306 /** 307 * Flag to indicate that cancel has been called on this object. This 308 * is useful if there's an intermediary that wants to first decode the 309 * bounds and then decode the image. In that case the intermediary 310 * can check, inbetween the bounds decode and the image decode, to see 311 * if the operation is canceled. 312 */ 313 public boolean mCancel; 314 315 /** 316 * This can be called from another thread while this options object is 317 * inside a decode... call. Calling this will notify the decoder that 318 * it should cancel its operation. This is not guaranteed to cancel 319 * the decode, but if it does, the decoder... operation will return 320 * null, or if inJustDecodeBounds is true, will set outWidth/outHeight 321 * to -1 322 */ 323 public void requestCancelDecode() { 324 mCancel = true; 325 requestCancel(); 326 } 327 } 328 329 /** 330 * Decode a file path into a bitmap. If the specified file name is null, 331 * or cannot be decoded into a bitmap, the function returns null. 332 * 333 * @param pathName complete path name for the file to be decoded. 334 * @param opts null-ok; Options that control downsampling and whether the 335 * image should be completely decoded, or just is size returned. 336 * @return The decoded bitmap, or null if the image data could not be 337 * decoded, or, if opts is non-null, if opts requested only the 338 * size be returned (in opts.outWidth and opts.outHeight) 339 */ 340 public static Bitmap decodeFile(String pathName, Options opts) { 341 Bitmap bm = null; 342 InputStream stream = null; 343 try { 344 stream = new FileInputStream(pathName); 345 bm = decodeStream(stream, null, opts); 346 } catch (Exception e) { 347 /* do nothing. 348 If the exception happened on open, bm will be null. 349 */ 350 Log.e("BitmapFactory", "Unable to decode stream: " + e); 351 } finally { 352 if (stream != null) { 353 try { 354 stream.close(); 355 } catch (IOException e) { 356 // do nothing here 357 } 358 } 359 } 360 return bm; 361 } 362 363 /** 364 * Decode a file path into a bitmap. If the specified file name is null, 365 * or cannot be decoded into a bitmap, the function returns null. 366 * 367 * @param pathName complete path name for the file to be decoded. 368 * @return the resulting decoded bitmap, or null if it could not be decoded. 369 */ 370 public static Bitmap decodeFile(String pathName) { 371 return decodeFile(pathName, null); 372 } 373 374 /** 375 * Decode a new Bitmap from an InputStream. This InputStream was obtained from 376 * resources, which we pass to be able to scale the bitmap accordingly. 377 */ 378 public static Bitmap decodeResourceStream(Resources res, TypedValue value, 379 InputStream is, Rect pad, Options opts) { 380 381 if (opts == null) { 382 opts = new Options(); 383 } 384 385 if (opts.inDensity == 0 && value != null) { 386 final int density = value.density; 387 if (density == TypedValue.DENSITY_DEFAULT) { 388 opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; 389 } else if (density != TypedValue.DENSITY_NONE) { 390 opts.inDensity = density; 391 } 392 } 393 394 if (opts.inTargetDensity == 0 && res != null) { 395 opts.inTargetDensity = res.getDisplayMetrics().densityDpi; 396 } 397 398 return decodeStream(is, pad, opts); 399 } 400 401 /** 402 * Synonym for opening the given resource and calling 403 * {@link #decodeResourceStream}. 404 * 405 * @param res The resources object containing the image data 406 * @param id The resource id of the image data 407 * @param opts null-ok; Options that control downsampling and whether the 408 * image should be completely decoded, or just is size returned. 409 * @return The decoded bitmap, or null if the image data could not be 410 * decoded, or, if opts is non-null, if opts requested only the 411 * size be returned (in opts.outWidth and opts.outHeight) 412 */ 413 public static Bitmap decodeResource(Resources res, int id, Options opts) { 414 Bitmap bm = null; 415 InputStream is = null; 416 417 try { 418 final TypedValue value = new TypedValue(); 419 is = res.openRawResource(id, value); 420 421 bm = decodeResourceStream(res, value, is, null, opts); 422 } catch (Exception e) { 423 /* do nothing. 424 If the exception happened on open, bm will be null. 425 If it happened on close, bm is still valid. 426 */ 427 } finally { 428 try { 429 if (is != null) is.close(); 430 } catch (IOException e) { 431 // Ignore 432 } 433 } 434 435 if (bm == null && opts != null && opts.inBitmap != null) { 436 throw new IllegalArgumentException("Problem decoding into existing bitmap"); 437 } 438 439 return bm; 440 } 441 442 /** 443 * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)} 444 * will null Options. 445 * 446 * @param res The resources object containing the image data 447 * @param id The resource id of the image data 448 * @return The decoded bitmap, or null if the image could not be decode. 449 */ 450 public static Bitmap decodeResource(Resources res, int id) { 451 return decodeResource(res, id, null); 452 } 453 454 /** 455 * Decode an immutable bitmap from the specified byte array. 456 * 457 * @param data byte array of compressed image data 458 * @param offset offset into imageData for where the decoder should begin 459 * parsing. 460 * @param length the number of bytes, beginning at offset, to parse 461 * @param opts null-ok; Options that control downsampling and whether the 462 * image should be completely decoded, or just is size returned. 463 * @return The decoded bitmap, or null if the image data could not be 464 * decoded, or, if opts is non-null, if opts requested only the 465 * size be returned (in opts.outWidth and opts.outHeight) 466 */ 467 public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) { 468 if ((offset | length) < 0 || data.length < offset + length) { 469 throw new ArrayIndexOutOfBoundsException(); 470 } 471 472 Bitmap bm; 473 474 Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); 475 try { 476 bm = nativeDecodeByteArray(data, offset, length, opts); 477 478 if (bm == null && opts != null && opts.inBitmap != null) { 479 throw new IllegalArgumentException("Problem decoding into existing bitmap"); 480 } 481 setDensityFromOptions(bm, opts); 482 } finally { 483 Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); 484 } 485 486 return bm; 487 } 488 489 /** 490 * Decode an immutable bitmap from the specified byte array. 491 * 492 * @param data byte array of compressed image data 493 * @param offset offset into imageData for where the decoder should begin 494 * parsing. 495 * @param length the number of bytes, beginning at offset, to parse 496 * @return The decoded bitmap, or null if the image could not be decode. 497 */ 498 public static Bitmap decodeByteArray(byte[] data, int offset, int length) { 499 return decodeByteArray(data, offset, length, null); 500 } 501 502 /** 503 * Set the newly decoded bitmap's density based on the Options. 504 */ 505 private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) { 506 if (outputBitmap == null || opts == null) return; 507 508 final int density = opts.inDensity; 509 if (density != 0) { 510 outputBitmap.setDensity(density); 511 final int targetDensity = opts.inTargetDensity; 512 if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) { 513 return; 514 } 515 516 byte[] np = outputBitmap.getNinePatchChunk(); 517 final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); 518 if (opts.inScaled || isNinePatch) { 519 outputBitmap.setDensity(targetDensity); 520 } 521 } else if (opts.inBitmap != null) { 522 // bitmap was reused, ensure density is reset 523 outputBitmap.setDensity(Bitmap.getDefaultDensity()); 524 } 525 } 526 527 /** 528 * Decode an input stream into a bitmap. If the input stream is null, or 529 * cannot be used to decode a bitmap, the function returns null. 530 * The stream's position will be where ever it was after the encoded data 531 * was read. 532 * 533 * @param is The input stream that holds the raw data to be decoded into a 534 * bitmap. 535 * @param outPadding If not null, return the padding rect for the bitmap if 536 * it exists, otherwise set padding to [-1,-1,-1,-1]. If 537 * no bitmap is returned (null) then padding is 538 * unchanged. 539 * @param opts null-ok; Options that control downsampling and whether the 540 * image should be completely decoded, or just is size returned. 541 * @return The decoded bitmap, or null if the image data could not be 542 * decoded, or, if opts is non-null, if opts requested only the 543 * size be returned (in opts.outWidth and opts.outHeight) 544 */ 545 public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) { 546 // we don't throw in this case, thus allowing the caller to only check 547 // the cache, and not force the image to be decoded. 548 if (is == null) { 549 return null; 550 } 551 552 Bitmap bm = null; 553 554 Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); 555 try { 556 boolean decodeGenericStream = true; 557 if (is instanceof AssetManager.AssetInputStream) { 558 final int asset = ((AssetManager.AssetInputStream) is).getAssetInt(); 559 bm = nativeDecodeAsset(asset, outPadding, opts); 560 // Do not follow the normal case. 561 decodeGenericStream = false; 562 } else if (is instanceof FileInputStream) { 563 try { 564 FileDescriptor fd = ((FileInputStream) is).getFD(); 565 // decodeFileDescriptor will take care of throwing the IAE and 566 // calling setDensityFromOptions. 567 return decodeFileDescriptor(fd, outPadding, opts); 568 } catch (IOException e) { 569 // Fall through to nativeDecodeStream. 570 } 571 } 572 573 if (decodeGenericStream) { 574 byte [] tempStorage = null; 575 if (opts != null) tempStorage = opts.inTempStorage; 576 if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; 577 bm = nativeDecodeStream(is, tempStorage, outPadding, opts); 578 } 579 580 if (bm == null && opts != null && opts.inBitmap != null) { 581 throw new IllegalArgumentException("Problem decoding into existing bitmap"); 582 } 583 584 setDensityFromOptions(bm, opts); 585 } finally { 586 Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); 587 } 588 589 return bm; 590 } 591 592 /** 593 * Decode an input stream into a bitmap. If the input stream is null, or 594 * cannot be used to decode a bitmap, the function returns null. 595 * The stream's position will be where ever it was after the encoded data 596 * was read. 597 * 598 * @param is The input stream that holds the raw data to be decoded into a 599 * bitmap. 600 * @return The decoded bitmap, or null if the image data could not be decoded. 601 */ 602 public static Bitmap decodeStream(InputStream is) { 603 return decodeStream(is, null, null); 604 } 605 606 /** 607 * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded 608 * return null. The position within the descriptor will not be changed when 609 * this returns, so the descriptor can be used again as-is. 610 * 611 * @param fd The file descriptor containing the bitmap data to decode 612 * @param outPadding If not null, return the padding rect for the bitmap if 613 * it exists, otherwise set padding to [-1,-1,-1,-1]. If 614 * no bitmap is returned (null) then padding is 615 * unchanged. 616 * @param opts null-ok; Options that control downsampling and whether the 617 * image should be completely decoded, or just its size returned. 618 * @return the decoded bitmap, or null 619 */ 620 public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) { 621 Bitmap bm; 622 623 Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor"); 624 try { 625 if (nativeIsSeekable(fd)) { 626 bm = nativeDecodeFileDescriptor(fd, outPadding, opts); 627 } else { 628 FileInputStream fis = new FileInputStream(fd); 629 // FIXME: If nativeDecodeStream grabbed the pointer to tempStorage 630 // from Options, this code would not need to be duplicated. 631 byte [] tempStorage = null; 632 if (opts != null) tempStorage = opts.inTempStorage; 633 if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; 634 try { 635 bm = nativeDecodeStream(fis, tempStorage, outPadding, opts); 636 } finally { 637 try { 638 fis.close(); 639 } catch (Throwable t) {/* ignore */} 640 } 641 } 642 643 if (bm == null && opts != null && opts.inBitmap != null) { 644 throw new IllegalArgumentException("Problem decoding into existing bitmap"); 645 } 646 647 setDensityFromOptions(bm, opts); 648 } finally { 649 Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); 650 } 651 return bm; 652 } 653 654 /** 655 * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded 656 * return null. The position within the descriptor will not be changed when 657 * this returns, so the descriptor can be used again as is. 658 * 659 * @param fd The file descriptor containing the bitmap data to decode 660 * @return the decoded bitmap, or null 661 */ 662 public static Bitmap decodeFileDescriptor(FileDescriptor fd) { 663 return decodeFileDescriptor(fd, null, null); 664 } 665 666 private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage, 667 Rect padding, Options opts); 668 private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, 669 Rect padding, Options opts); 670 private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts); 671 private static native Bitmap nativeDecodeByteArray(byte[] data, int offset, 672 int length, Options opts); 673 private static native boolean nativeIsSeekable(FileDescriptor fd); 674} 675