0、写在前面
| density | 1 | 1.5 | 2 | 3 | 3.5 | 4 |
| densityDpi | 160 | 240 | 320 | 480 | 560 | 640 |
1、占了多大内存?
|
1
2
3
4
|
public final int getByteCount() { // int result permits bitmaps up to 46,340 x 46,340 return getRowBytes() * getHeight();} |
2、给我一张图我告诉你占多大内存
2.1 getByteCount
|
1
2
3
4
5
6
|
public final int getrowBytes() { if (mRecycled) { Log.w(TAG, "Called getRowBytes() on a recycle()‘d bitmap! This is undefined behavior!"); } return nativeRowBytes(mFinalizer.mNativeBitmap);} |
|
1
2
3
4
|
static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle) return static_cast<jint>(bitmap->rowBytes());} |
|
1
2
|
/** Return the number of bytes between subsequent rows of the bitmap. */size_t rowBytes() const { return fRowBytes; } |
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
size_t SkBitmap::ComputeRowBytes(Config c, int width) { return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width);}SkImageInfo.hstatic int SkColorTypeBytesPerPixel(SkColorType ct) { static const uint8_t gSize[] = { 0, // Unknown 1, // Alpha_8 2, // RGB_565 2, // ARGB_4444 4, // RGBA_8888 4, // BGRA_8888 1, // kIndex_8 }; SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1), size_mismatch_with_SkColorType_enum); SkASSERT((size_t)ct < SK_ARRAY_COUNT(gSize)); return gSize[ct];}static inline size_t SkColorTypeMinRowBytes(SkColorType ct, int width) { return width * SkColorTypeBytesPerPixel(ct);} |
2.2 Density
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public static Bitmap decodeResourceStream(Resources res, TypedValue value, InputStream is, Rect pad, Options opts) {//实际上,我们这里的opts是null的,所以在这里初始化。if (opts == null) { opts = new Options();}if (opts.inDensity == 0 && value != null) { final int density = value.density; if (density == TypedValue.DENSITY_DEFAULT) { opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; } else if (density != TypedValue.DENSITY_NONE) { opts.inDensity = density; //这里density的值如果对应资源目录为hdpi的话,就是240 }}if (opts.inTargetDensity == 0 && res != null) {//请注意,inTargetDensity就是当前的显示密度,比如三星s6时就是640 opts.inTargetDensity = res.getDisplayMetrics().densityDpi;}return decodeStream(is, pad, opts);} |
|
1
2
3
4
5
|
public Options() { inDither = false; inScaled = true; inPremultiplied = true;} |
|
01
02
03
04
05
06
07
08
09
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
|
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {...... if (env->GetBooleanField(options, gOptions_scaledFieldID)) { const int density = env->GetIntField(options, gOptions_densityFieldID);//对应hdpi的时候,是240 const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);//三星s6的为640 const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID); if (density != 0 && targetDensity != 0 && density != screenDensity) { scale = (float) targetDensity / density; } }}const bool willScale = scale != 1.0f;......SkBitmap decodingBitmap;if (!decoder->decode(stream, &decodingBitmap, prefColorType,decodeMode)) { return nullObjectReturn("decoder->decode returned false");}//这里这个deodingBitmap就是解码出来的bitmap,大小是图片原始的大小int scaledWidth = decodingBitmap.width();int scaledHeight = decodingBitmap.height();if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) { scaledWidth = int(scaledWidth * scale + 0.5f); scaledHeight = int(scaledHeight * scale + 0.5f);}if (willScale) { const float sx = scaledWidth / float(decodingBitmap.width()); const float sy = scaledHeight / float(decodingBitmap.height()); // TODO: avoid copying when scaled size equals decodingBitmap size SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType()); // FIXME: If the alphaType is kUnpremul and the image has alpha, the // colors may not be correct, since Skia does not yet support drawing // to/from unpremultiplied bitmaps. outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, colorType, decodingBitmap.alphaType())); if (!outputBitmap->allocPixels(outputAllocator, NULL)) { return nullObjectReturn("allocation failed for scaled bitmap"); } // If outputBitmap‘s pixels are newly allocated by Java, there is no need // to erase to 0, since the pixels were initialized to 0. if (outputAllocator != &javaAllocator) { outputBitmap->eraseColor(0); } SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); SkCanvas canvas(*outputBitmap); canvas.scale(sx, sy); canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);}......} |
2.3 精度
|
1
2
|
outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, colorType, decodingBitmap.alphaType())); |
|
1
2
3
4
|
if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) { scaledWidth = int(scaledWidth * scale + 0.5f); scaledHeight = int(scaledHeight * scale + 0.5f);} |
“源码之前,了无秘密”。
2.4 小结
3、想办法减少 Bitmap 内存占用
3.1 Jpg 和 Png
『啪!!!』『谁这么缺德!!打人不打脸好么!』
JPG 不适用于所含颜色很少、具有大块颜色相近的区域或亮度差异十分明显的较简单的图片。对于需要高保真的较复杂的图像,PNG 虽然能无损压缩,但图片文件较大。
3.2 使用 inSampleSize
|
1
2
3
|
BitmapFactory.Options options = new Options();options.inSampleSize = 2;Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId, options); |
3.3 使用矩阵
『基友』『是在下输了。。』
|
1
2
3
4
5
|
Matrix matrix = new Matrix();matrix.preScale(2, 2, 0f, 0f);//如果使用直接替换矩阵的话,在Nexus6 5.1.1上必须关闭硬件加速canvas.concat(matrix);canvas.drawBitmap(bitmap, 0,0, paint); |
|
1
2
3
|
Matrix matrix = new Matrix();matrix.preScale(2, 2, 0, 0);canvas.drawBitmap(bitmap, matrix, paint); |
|
1
2
3
4
5
|
Matrix matrix = new Matrix();matrix.postScale(2, 2, 0, 0);imageView.setImageMatrix(matrix);imageView.setScaleType(ScaleType.MATRIX);imageView.setImageBitmap(bitmap); |
3.4 合理选择Bitmap的像素格式
| 格式 | 描述 |
| ALPHA_8 | 只有一个alpha通道 |
| ARGB_4444 | 这个从API 13开始不建议使用,因为质量太差 |
| ARGB_8888 | ARGB四个通道,每个通道8bit |
| RGB_565 | 每个像素占2Byte,其中红色占5bit,绿色占6bit,蓝色占5bit |
3.5 高能:索引位图(Indexed Bitmap)
|
01
02
03
04
05
06
07
08
09
10
|
public enum Config { // these native values must match up with the enum in SkBitmap.h ALPHA_8 (2), RGB_565 (4), ARGB_4444 (5), ARGB_8888 (6); final int nativeInt;} |
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
enum Config { kNo_Config, //!< bitmap has not been configured kA8_Config, //!< 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque) //看这里看这里!!↓↓↓↓↓ kIndex8_Config, //!< 8-bits per pixel, using SkColorTable to specify the colors kRGB_565_Config, //!< 16-bits per pixel, (see SkColorPriv.h for packing) kARGB_4444_Config, //!< 16-bits per pixel, (see SkColorPriv.h for packing) kARGB_8888_Config, //!< 32-bits per pixel, (see SkColorPriv.h for packing) kRLE_Index8_Config, kConfigCount}; |
|
01
02
03
04
05
06
07
08
09
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
|
bool SkPNGImageDecoder::getBitmapColorType(png_structp png_ptr, png_infop info_ptr, SkColorType* colorTypep, bool* hasAlphap, SkPMColor* SK_RESTRICT theTranspColorp) {png_uint_32 origWidth, origHeight;int bitDepth, colorType;png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, &colorType, int_p_NULL, int_p_NULL, int_p_NULL);#ifdef PNG_sBIT_SUPPORTED // check for sBIT chunk data, in case we should disable dithering because // our data is not truely 8bits per component png_color_8p sig_bit; if (this->getDitherImage() && png_get_sBIT(png_ptr, info_ptr, &sig_bit)) {#if 0 SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green, sig_bit->blue, sig_bit->alpha);#endif // 0 seems to indicate no information available if (pos_le(sig_bit->red, SK_R16_BITS) && pos_le(sig_bit->green, SK_G16_BITS) && pos_le(sig_bit->blue, SK_B16_BITS)) { this->setDitherImage(false); }}#endifif (colorType == PNG_COLOR_TYPE_PALETTE) { bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr); *colorTypep = this->getPrefColorType(kIndex_SrcDepth, paletteHasAlpha); // now see if we can upscale to their requested colortype //这段代码,如果返回false,那么colorType就被置为索引了,那么我们看看如何返回false if (!canUpscalePaletteToConfig(*colorTypep, paletteHasAlpha)) { *colorTypep = kIndex_8_SkColorType; }} else {...... }return true;} |
|
01
02
03
04
05
06
07
08
09
10
11
12
|
static bool canUpscalePaletteToConfig(SkColorType dstColorType, bool srcHasAlpha) { switch (dstColorType) { case kN32_SkColorType: case kARGB_4444_SkColorType: return true; case kRGB_565_SkColorType: // only return true if the src is opaque (since 565 is opaque) return !srcHasAlpha; default: return false;}} |
|
01
02
03
04
05
06
07
08
09
10
|
try { Options options = new Options(); options.inPreferredConfig = Config.RGB_565;Bitmap bitmap = BitmapFactory.decodeStream(getResources().getAssets().open("index.png"), null, options); Log.d(TAG, "bitmap.getConfig() = " + bitmap.getConfig()); Log.d(TAG, "scaled bitmap.getByteCount() = " + bitmap.getByteCount()); imageView.setImageBitmap(bitmap);} catch (IOException e) { e.printStackTrace();} |
|
1
2
|
D/MainActivity: bitmap.getConfig() = nullD/MainActivity: scaled bitmap.getByteCount() = 36864 |
public final Bitmap.Config getConfig ()Added in API level 1If the bitmap’s internal config is in one of the public formats, return that config, otherwise return null.
3.6 不要辜负。。。『哦,不要姑父!』
『排期太紧了,这些给我出一系列图吧』『好,不过每张图都是 300*30 0的 png 哈,总共 5 张,为了适配不同的分辨率,需要出 xxhdpi 和 xxxhdpi 的两套图。。』
4、结语
Android 开发绕不过的坑:你的 Bitmap 究竟占多大内存?
原文:http://www.cnblogs.com/krislight1105/p/5203277.html