0%

Bitmap 的加载和 Cache(一):Bitmap 的高效加载

1. Bitmap 概念概述

  • 概念

    • 位图(Bitmap,台湾称点阵图),又称栅格图(Raster graphics),是使用像素阵列(Pixel-array/Dot-matrix 点阵)来表示的图像。在 Android 中指的是一张图片,可以是 png 格式也可以是 jpg 等其他常见的图片格式
    • 位图的像素都分配有特定的位置颜色值,每个像素的颜色信息由 RGB 组合或者灰度值表示
      • 根据位深度,可将位图分为 1、4、8、16、24 及 32 位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大
      • RGB 图像由三个颜色通道组成。8 位/通道的 RGB 图像中的每个通道有 256 个可能的值,这意味着该图像有 1600 万个以上可能的颜色值。有时将带有 8 位/通道(bpc)的 RGB 图像称为 24 位图像(8 位 * 3 通道 = 24 位数据/像素)。通常将使用 24 位 RGB 组合数据位表示的位图称为真彩色位图
  • 编码方式

    • RGB,位图颜色的一种编码方法,用红、绿、蓝三原色的光学强度来表示一种颜色。这是最常见的位图编码方法,可以直接用于屏幕显示
    • CMYK,位图颜色的一种编码方法,用青、品红、黄、黑四种颜料含量来表示一种颜色。常用的位图编码方法之一,可以直接用于彩色印刷
  • 索引颜色/颜色表

    • 位图是一种常用的压缩方法。从位图图片中选择最有代表性的若干种颜色(通常不超过 256 种)编制成颜色表,然后将图片中原有颜色用颜色表的索引来表示,这样原图可以被大幅度有损压缩
    • 有损压缩适合于压缩图形等颜色数较少的图形,不适合压缩照片等色彩丰富的图形
  • Alpha 通道

    • 在原有的图片编码方法基础上,增加像素的透明度信息
    • 图形处理中,通常把 RGB 三种颜色信息称为红通道绿通道蓝通道,相应的把透明度称为 Alpha 通道,多数使用颜色表的位图格式都支持 Alpha 通道
  • 色彩深度

    • 色彩深度又叫色彩位数,即位图中要用多少个二进制位来表示每个点的颜色,是分辨率的一个重要指标
    • 常用有 1 位(单色),2 位(4 色,CGA),4 位(16 色,VGA),8 位(256 色),16 位(增强色),24 位和 32 位(真彩色)等
  • Bitmap 源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    /**
    * Copyright (C) 2006 The Android Open Source Project
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */
    package android.graphics;

    // import bla bla bla

    public final class Bitmap implements Parcelable {
    private static final String TAG = "Bitmap";

    /**
    * Indicates that the bitmap created for an unknown pixel density.
    *
    * @see Bitmap#getDensity()
    * @see Bitmap#setDensity()
    */
    public static final int DENSITY_NONE = 0;

    // Estimated size of the Bitmap native allocation, not including
    // pixel data.
    private static final long NATIVE_ALLOCATION_SIZE = 32;

    // Convenience for JNI access
    @UnsupportedAppUsage
    private final long mNativePtr;

    /**
    * bla bla bla
    */
    private boolean mRequestPremultiplied;

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODE.P, trackingBut = 123769491)
    private byte[] mNinePatchChunk; // may be null
    @UnsupportedAppUsage
    private NinePatch.InsetStruct mNinePatchInsets; // may be null
    @UnsupportedAppUsage
    private int mWidth;
    @UnsupportedAppUsage
    private int mHeight;
    private boolean mRecycled;

    private ColorSpace mColorSpace;

    // bla bla bla

    /**
    * Possible bitmap configurations. A bitmap configuraion describes
    * how pixels are stored. This affects the quality (color depth) as
    * well as the ability to display transparent/translucent colors.
    */
    public enum Config {
    // these native values must match up with the enum in SkBitmap.h

    /**
    * Each pixel is stored as a single translucency (alpha) channel.
    * This is very useful to efficiently store masks for instance.
    * No color information is stored.
    * With this configuration, each pixel requires 1 byte of memory.
    */
    ALPHA_8 (1),

    /**
    * Each pixel is stored on 2 bytes and only the RGB channels are
    * encoded: red is stored with 5 bits of precision (32 possible
    * values), green is stored with 6 bits of precision (64 possible
    * values) and blue is stored with 5 bits of precision.
    *
    * This configuration can produce slight visual artifacts depending
    * on the configuration of the source. For instance, without
    * dithering, the result might show a greenish tint. To get better
    * results dithering should be applied.
    *
    * This configuration may be useful when using opaque bitmaps
    * that do not require high color fidelity.
    *
    * <p>Use this formula to pack into 16 bits:</p>
    * <pre class="prettyprint">
    * short color = (R & 0x1f) << 11 | (G & 0x3f) << 5 | (B & 0x1f);
    * </pre>
    */
    RGB_565 (3),

    /**
    * Each pixel is stored on 2 bytes. The three RGB color channels
    * and the alpha channel (translucency) are stored with a 4 bits
    * precision (16 possible values.)
    *
    * This configuration is mostly useful if the application needs
    * to store translucency information but also needs to save
    * memory.
    *
    * It is recommended to use {@link #ARGB_8888} instead of this
    * configuration.
    *
    * Note: as of {@link android.os.Build.VERSION_CODES#KITKAT},
    * any bitmap created with this configuration will be created
    * using {@link #ARGB_8888} instead.
    *
    * @deprecated Because of the poor quality of this configuration,
    * it is advised to use {@link #ARGB_8888} instead.
    */
    @Deprecated
    ARGB_4444 (4),

    /**
    * Each pixel is stored on 4 bytes. Each channel (RGB and alpha
    * for translucency) is stored with 8 bits of precision (256
    * possible values.)
    *
    * This configuration is very flexible and offers the best
    * quality. It should be used whenever possible.
    *
    * <p>Use this formula to pack into 32 bits:</p>
    * <pre class="prettyprint">
    * int color = (A & 0xff)) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff);
    * </pre>
    */
    ARGB_8888 (5),

    /**
    * Each pixels is stored on 8 bytes. Each channel (RGB and alpha
    * for translucency) is stored as a
    * {@link android.util.Half half-precision floating point value}.
    *
    * This configuration is particularly suited for wide-gamut and
    * HDR content.
    *
    * <p>Use this formula to pack into 64 bits:</p>
    * <pre class="prettyprint">
    * long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff);
    * </pre>
    */
    RGBA_F16 (6),

    /**
    * Special configuration, when bitmap is stored only in graphic memory.
    * Bitmap in this configuration are always immutable.
    *
    * It is optimal for cases, when the only operation with the bitmap is to draw it on a
    * screen.
    */
    HARDWARE (7);

    @UnsupportedAppUsage
    final int nativeInt;

    private static Config sConfigs[] = {
    null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE
    };

    Config(int ni) {this.nativeInt = ni;}

    @UnsupportedAppUsage
    static Config nativeToConfig(int ni) {
    return sConfigs[ni];
    }
    }

    // bla bla bla

    /**
    * Specifies the known formats a bitmap can be compressed into
    */
    public enum CompressFormat {
    /**
    * Compress to the JPEG format. {@code quality} of {@code 0} means
    * compress for the smallest size. {@code 100} means compress for max
    * visual quality.
    */
    JPEG (0),
    /**
    * Compress to the PNG format. PNG is lossless, so {@code quality} is
    * ignored.
    */
    PNG (1),
    /**
    * Compress to the WEBP format. {@code quality} of {@code 0} means
    * compress for the smallest size. {@code 100} means compress for max
    * visual quality. As of {@link android.os.Build.VERSION_CODES#Q}, a
    * value of {@code 100} results in a file in the lossless WEBP format.
    * Otherwise the file will be in the lossy WEBP format.
    *
    * @deprecated in favor of the more explicit
    * {@link CompressFormat#WEBP_LOSSY} and
    * {@link CompressFormat#WEBP_LOSSLESS}.
    */
    @Deprecated
    WEBP (2),
    /**
    * Compress to the WEBP lossy format. {@code quality} of {@code 0} means
    * compress for the smallest size. {@code 100} means compress for max
    * visual quality.
    */
    WEBP_LOSSY (3),
    /**
    * Compress to the WEBP lossless format. {@code quality} refers to how
    * much effort to put into compression. A value of {@code 0} means to
    * compress quickly, resulting in a relatively large file size.
    * {@code 100} means to spend more time compressing, resulting in a
    * smaller file.
    */
    WEBP_LOSSLESS (4);

    CompressFormat(int nativeInt) {this.nativeInt = nativeInt;}
    final int nativeInt;
    }

    // bla bla bla

    /** Returns the bitmap's height */
    public final int getHeight() {
    if (mRecycled) {
    Log.w(TAG, "Called getHeight() on a recycle()'d bitmap! This is undefined behavior!");
    }
    return mHeight;
    }

    // bla bla bla

    /**
    * Return the number of bytes between rows in the bitmap's pixels. Note that
    * this refers to the pixels as stored natively by the bitmap. If you call
    * getPixels() or setPixels(), then the pixels are uniformly treated as
    * 32bit values, packed according to the Color class.
    *
    * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, this method
    * should not be used to calculate the memory usage of the bitmap. Instead,
    * see {@link #getAllocationByteCount()}.
    *
    * @return number of bytes between rows of the native bitmap pixels.
    */
    public final int getRowBytes() {
    if (mRecycled) {
    Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!");
    }
    return nativeRowBytes(mNativePtr);
    }

    /**
    * Returns the minimum number of bytes that can be used to store this bitmap's pixels.
    *
    * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, the result of this method can
    * no longer be used to determine memory usage of a bitmap. See {@link
    * #getAllocationByteCount()}.</p>
    */
    public final int getByteCount() {
    if (mRecycled) {
    Log.w(TAG, "Called getByteCount() on a recycle()'d bitmap!" + "This is undefined behavior!");
    return 0;
    }
    // int result permits bitmaps up to 46,340 * 46,340
    return getRowBytes() * getHeight();
    }

    // bla bla bla

    /**
    * Returns the size of the allocated memory used to store this bitmap's pixels.
    *
    * <p>This can be larger than the result of {@link #getByteCount()} if a bitmap is reused to
    * decode other bitmaps of smaller size, or by manual reconfiguration. See {@link
    * #reconfigure(int, int, Config)}, {@link #setWidth(int)}, {@link #setHeight(int)}, {@link
    * #setConfig(Bitmap.Config)}, and {@link BitmapFactory.Options#inBitmap
    * BitmapFactory.Options.inBitmap}. If a bitmap is not modified in this way, this value will be
    * the same as that returned by {@link #getByteCount()}.</p>
    *
    * <p>This value will not change over the lifetime of a Bitmap.</p>
    *
    * @see #reconfigure(int, int, Config)
    */
    public final int getAllocationByteCount() {
    if (mRecycled) {
    Log.w(TAG, "Called getAllocationByteCount() on a recycle()'d bitmap!" + "This is undefined behavior!");
    return 0;
    }
    return nativeGetAllocationByteCount(mNativePtr);
    }

    // bla bla bla
    }
  • 官方:减小图片下载大小

2. 在 Android 中加载位图比较复杂的原因

  • 位图很容易就会耗尽应用的内存预算。例如,Pixel 手机上的相机拍摄的照片最大可达 4048*3036 像素(1200 万像素)。如果使用的位图配置为 ARGB_8888(Android 2.3/API 9 及更高版本的默认设置),将单张照片加载到内存大约需要 48 MB 内存(4048*0336*4 字节)。如此庞大的内存需要可能会立即耗尽该应用的所有可用内存
  • 在界面线程中加载位图会降应用的性能,导致响应速度变慢,甚至会导致 ANR。因此,在使用位图时,必须正确地管理线程处理
  • 应用加载多个位图到内存时,需要娴熟地管理内存缓存磁盘缓存。否则,应用界面的响应速度和流畅性可能会受影响
  • 综上,Google 官方建议使用 Glide 获取、解码和显示应用中的位图。在处理这些任务以及与位图和 Android 上的其他图片相关的其他任务时,Glide 会将大部分的复杂工作抽象出来

3. 图片压缩原理概述

  • 空间冗余

    • 利用图像上各采样点颜色之间存在着的空间连贯性,把本该一个一个像素存储的数据,合并压缩存储加载时进行解压还原
    • 通常无损压缩利用的就是空间冗余原理
  • 视觉冗余

    • 人类的视觉系统由于受生理特性的限制,对于图像场的注意是非均匀的,人对细微的颜色差异感觉不明显。例如,人类视觉的一般分辨能力为 26 灰度等级,而一般的图像的量化采用的是 28 灰度等级,即存在视觉冗余
    • 通常有损压缩利用的是人的视觉冗余原理,擦除了对人的眼睛来说冗余的信息
  • 参考:抖音包大小优化-资源优化

4. 高效加载大型位图策略

  1. 读取位图尺寸和类型

    • BitmapFactory 类提供了几种用于从各种来源创建 Bitmap 的解码方法:decodeByteArray()decodeFile()decodeResource()decodeResourceStream()decodeStream()decodeFileDescriptor() 等,根据图片数据源选择最合适的解码方法

    • 这些方法尝试为构造的位图分配内存,因此很容易导致 OutOfMemory 异常。每种类型的解码方法都有额外的签名,允许通过 BitmapFactory.Options 类指定解码选项

    • 在解码时将 inJustDecodeBounds 属性设置为 true 可避免内存分配,为位图对象返回 null,但会设置 outWidth、outHeight 和 outMimeType。此方法可在构造位图并为其分配内存之前读取图片数据的尺寸和类型

      1
      2
      3
      4
      5
      6
      BitmapFactory.Options options = new BitmapFactory.Options();
      options.inJustDecodeBounds = true;
      BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
      int imageHeight = options.outHeight;
      int imageWidth = options.outWidth;
      String imageType = options.outMimeType;
      • 为避免出现 java.lang.OutOfMemory 异常,应该先检查位图的尺寸,然后再对其进行解码,除非绝对信任该来源可提供大小可预测的图片数据
  2. 将按比例缩小的版本加载到内存中

    • 当图片尺寸已知,就可以确定应将完整尺寸的图片加载到内存中,还是加载下采样版本。需要考虑如下几个因素:

      • 在内存中加载完整图片的估计内存使用量
      • 根据应用的任何其他内存要求,开发者愿意分配用于加载此图片的内存量
      • 图片要载入到的目标 ImageView 或界面组件的尺寸
      • 当前设备的屏幕大小和密度
    • 要让解码器对图片进行下采样,以将较小版本加载到内存中,应在 BitmapFactory.Options 对象中将 inSampleSize 设置为 true

      • 例如,分辨率为 2048*1536 且以 4 作为 inSampleSize 进行解码的图片会生成大约 512384 的位图。将此图片加载到内存中需使用 0.75 MB,而不是完整图片所需的 12 MB(假设位图配置为 *ARGB_8888**)
    • 下面的方法用于计算下采样率值,即基于目标宽度和高度的 2 的幂:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
      // Raw height and width of image
      final int height = options.outHeight;
      final int width = options.outWidth;
      int inSampleSize = 1;

      if (height > reqHeight || width > reqWidth) {
      final int halfHeight = height / 2;
      final int halfWidth = height / 2;

      // Calculate the largest inSampleSize value that is a power of 2 and keeps both
      // height and width larger than the requested height and width.
      while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
      inSampleSize *= 2;
      }
      }

      return inSampleSize;
      }
      • 根据 inSampleSize 文档,计算 2 的幂的原因是解码器使用的最终值将向下舍入为最接近的 2 的幂
    • 要使用方法 calculateInSampleSize(),需要先将 inJustDecodeBounds 设为 true 进行解码,传递 BitmapFactory.Options 参数,然后使用新的 inSampleSize 并将 inJustDecodeBounds 设为 false 再次进行解码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      public static Bitmap decodeSampledBitmapFromResource(Resource res, int resId, int reqWidth, int reqHeight) {

      // 1. First decode with inJustDecodeBounds = true to check dimentions
      final BitmapFactory.Options options = new BitmapFactory.Options();
      options.inJustDecodeBounds = true;
      BitmapFactory.decodeResource(res, resId, options);

      // 2. Calculate inSampleSize
      options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

      // 3. Decode bitmap with inSampleSzie set
      options.inJustDecodeBounds = false;
      return BitmapFactory.decodeResource(res, resId, options);
      }
    • 采用上面方法,可以轻松地将任意大尺寸的位图加载到显示 100*100 像素缩略图的 ImageView 中,如下 Demo:

      1
      imageView.setImageBitmap(decodeSampledBitmapFromResource(getResource(), R.id.myimage, 100, 100));
      • 可以按照类似的流程来解码其他来源的位图,只需根据需要替换相应的 BitmapFactory.decode*() 方法即可(decodeStream() 方法有点特殊)

5. BitmapFactory 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package android.graphics;

// import bla bla bla

/**
* Creates Bitmap objects from sources, including files, streams,
* and byte-arrays.
*/
public class BitmapFactory {
private static final int DECODE_BUFFER_SIZE = 16 * 1024;

public static class Options {
/**
* Create a default Options object, which if left unchanged will give
* the same result from the decode as if null were passed.
*/
public Options() {
inScaled = true;
inPremultiplied = true;
}

// bla bla bla

/**
* If set to true, the decoder will return null (no bitmap), but
* the <code>out...</code> fields will still be set, allowing the caller to
* query the bitmap without having to allocate the memory for its pixels.
*/
public boolean inJustDecodeBounds;

/**
* If set to a value > 1, requests the decoder to subsample the original
* image, returning a smaller image to save memory. The sample size is
* the number of pixels in either dimension that correspond to a single
* pixel in the decoded bimap. For example, inSampleSize == 4 returns
* an image that is 1/4 the width/height of the original, and 1/16 the
* number of pixels. Any value <= 1 is treated the same as 1. Note: the
* decoder uses a final value based on powers of 2, any other value will
* be rounded down to the nearest power of 2.
*/
public int inSampleSize;

// bla bla bla

/**
* The resulting width of the bitmap. If {@link #inJustDecodeBounds} is
* set to false, this will be width of the output bitmap after any
* scaling is applied. If true, it will be the width of the input image
* without any accounting for scaling.
*
* <p>outWidth will be set to -1 if there is an error trying to decode.</p>
*/
public int outWidth;

/**
* The resulting height of the bitmap. If {@link #inJustDecodeBounds} is
* set to false, this will be height of the output bitmap after any
* scaling is applied. If true, it will be the height of the input image
* without any accounting for scaling.
*
* <p>outHeight will be set to -1 if there is an error trying to decode.</p>
*/
public int outHeight;

/**
* If known, this string is set to the mimetype of the decoded image.
* If not known, or there is an error, it is set to null.
*/
public String outMimeType;

/**
* If known, this config the decoded bitmap will have.
* If not known, or there is an error, it is set to null.
*/
public Bitmap.Config outConfig;

// bla bla bla
}

// bla bla bla

/**
* Decode an input stream into a bitmap. If the input stream is null, or
* cannot be used to decode a bitmap, the function returns null.
* The stream's position will be where ever it was after the encoded data
* was read.
*
* @param is The input stream that holds the raw data to be decoded into a
* bitmap.
* @param outPadding If not null, return the padding rect for the bitmap if
* it exists, otherwise set padding to [-1,-1,-1,-1]. If
* no bitmap is returned (null) then padding is
* unchanged.
* @param opts null-ok; Options that control downsampling and whether the
* image should be completely decoded, or just is size returned.
* @return The decoded bitmap, or null if the image data could not be
* decoded, or, if opts is non-null, if opts requested only the
* size be returned (in opts.outWidth and opts.outHeight)
* @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
* is {@link android.graphics.Bitmap.Config#HARDWARE}
* and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
* is not {@link ColorSpace.Mode#RGB RGB}, or if the specified color space's transfer
* function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
* d
* <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
* if {@link InputStream#markSupported is.markSupported()} returns true,
* <code>is.mark(1024)</code> would be called. As of
* {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
*/
@Nullable
public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding, @Nullable Options opts) {
// we don't throw in this case, thus allowing the caller to only check
// the cache, and not force the image to be decoded.
if (is == null) {
return null;
}
validate(opts);

Bitmap bm = null;

Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
try {
if (is instanceof AssetManager.AssetInputStream) {
final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
bm = nativeDecodeAsset(asset, outPadding, opts, Options.nativeInBitmap(opts), Options.nativeColorSpace(opts));
} else {
bm = decodeStreamInternal(is, outPadding, opts);
}

if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap);
}

setDensityFromOptions();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
}

return bm;
}
}
-------------------- 本文结束感谢您的阅读 --------------------