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.h static 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 ); } } #endif if (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() = null D/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