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、结语