Bitmap 笔记

27 阅读5分钟

Bitmap 在绘图中主要有两种:

1).转换为BitmapDrawable对象使用

Bitmap bmp = BitmapFactory.decodeResource(res,R.drawable.icon);
BitmapDrawable bmpDraw = new BitMapDrawable(bmp);
iv.setImageDrawable(bmpDraw);

2)当作画布使用

1.默认画布 onDraw(Canvas canvas) 2.自建画布 new Canvas(bitmap)

一张位图所占内存 = 图片长度(px) * 图片宽度(px) * 一个像素点所占用的字节。

类型
ALPHA_88 位 Alpha 位图 (没有颜色,只有透明度)一个像素占1字节
ARGB_444416位ARGB位图一个像素 16位 2字节
ARGB_888832位ARGB位图一个像素 32位 4字节
RGB_56516位RGB位图(没有透明度)一个像素 16位 2字节

//一般建议使用ARGB_8888格式来存储Bitmap,如果没有透明度要求,可以使用RGB_565节省内存开销。

内存中存储Bitmap对象大小: Bitmap的宽Bitmap的高每个像素所占大小。

文件中的Bitmap大小: 根据Jpg,png 格式来压缩。

创建Bitmap方法: 一、BitmapFactory

1.decodeResource(Resources res, int id) //用来从资源文件中解码一张位图

2.decodeFile(String pathName) //通过文件路径来加载图片。pathName是全路径名。

3.decodeByteArray(byte[] data, int offset, int length) //根据byte数组来解析Bitmap,data压缩图像数据的字节数组,offset 图像数据偏移量,length字节数。

一般使用步骤:

  • 开启异步去请求图片
  • 网络返回inputStream
  • 把inputStream 转 byte[]
  • 解析 Btimap bm = BitmapFactory.decodeByteArray(byte,0,byte.length)

4.decodeFileDescriptor

public static Bitmap decodeFileDescriptor(FileDescriptor fd)
public static Bitmap decodeFileDescriptor(FileDescriptor fd,Rect outPadding,Options opts)
//fd:包含解码位图数据的文件路径。outPadding 用于返回矩形的内边距,没有返回成功返回(-1,-1,-1,-1)

decodeFileDescriptor相比decodeFile会更节省内存,前者直接调用Native层函数,由Native层解析出Bitmap返回。后者在调用nativeDecodeStream前,可能会申请两次空间。

5.decodeStream

public static Bitmap decodeStream(InputStream is)
public static Bitmap decodeStream(inputStream is,Rect outPadding,Options)

//这个方法也可以用来加载网络图片

BitmapFactory.Options

1.inJustDecodeBounds 获取图片信息, true 表示只解析图片信息,不获取图片,不分配内存

BitmapFactory.Options op = new BitmapFactory.Options()
op.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.DecodeRecourse(getRecourse(),R.drawable.icon,op);

//宽 options.outWidth,高 options.outHeight,MiME类型 options.outMimeType

2.inSampleSize 压缩图片.采样率,每多少像素作为一个结果返回,其余的被抛弃。官方建议取2的幂数。设置时应注意使得缩放后的图片尺寸尽量大于等于相应的ImageView大小(拿原始宽高和目标宽高相比,取宽比,高比两个比值中最小的)

屏幕上1英寸对应的px数 = 每英寸对应的dpi数 * dpi对应的px数

加载资源文件中的图片。动态缩放图片:缩放比例 = 屏幕分辨率/文件所在文件夹的分辨率

加载SD文件中的图片:不进行缩放。

3.inScaled 缩放 false表示不缩放 默认为true

inDensity 设置文件所在资源文件夹的屏幕分辨率

inTargetDesity 真实显示的屏幕分辨率

scale = inTargetDensity / inDensity

4.inPreferredConfig 设置像素存储格式

二、Bitmap静态创建法

createBitmap 方法

在加载或创建Bitmap时,要放在try catch中捕捉异常,防止oom

通过BitmapFactoory加载的Bitmap都是像素不可更改的,只有通过Bitmap中的几个函数创建的bitmap像素才可以更改。如:

copy(Bitmap.Config config, boolean isMutable) 
createBitmap(int width, int height, Bitmap.Config config) 
createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) 
//在 API 17 中引入 
createBitmap(DisplayMetrics display, int width, int height, Bitmap.Config config)

可以通过isMutabel()来判断像素是否可以更改。如果不能更改,setPixel()会报错。

注意:只有上面几个方法是像素可更改的,像素不能更改的图像,是不能作为画布的(new Canvas(Bitmap))

extractAlpha() 从Bitmap中抽取出alpha值,返回只有alpha值得图像,存储格式alpha_8

extractAlpha(Paint paint,int[] offsetXY) // paint具有MaskFilter效果得paint对象,一般用BLurMaskFilter模糊效果。offsetXY 返回添加BlurMaskFilter效果后远点得偏移量。在绘制得时候需要把这个偏移量考虑进去,比如6px得模糊效果 ,那么绘制时候不能从(0,0)开始,要从(-6,-6)开始计算,所以offsetXY值为[-6,-6],那么源图像绘制就需要canvas.drawBitmap(srcBtimap,-offsetXY[0],-offsetXY[1],null)

onFinishInflate()函数调用时机是系统将XML解析出对应得控件实例得时候。

Bitmap获取分配空间

getAllocationByteCount() // API>=19以上需要使用这个函数。

getByteCount() // api>=12 && api< 19 用这个函数

getRowBytes() // api<12 用这个函数 内存 = getRowBytes() * bitmap.getHeight()

  • recycle() 、isRecycled()//使用已被recycle得bitmap会crash.

  • setDensity()、getDensity() // 对应inDensity。

  • setPixel(int x,int y,int color)、getPixel() //针对Bitmap中某个位置的像素进行设置和获取。

-boolean compress(CompressFormat format, int quality, OutputStream stream) //压缩图像,将压缩的图像写入输出流。format 压缩格式。quality质量 0-100, png等无损格式,忽略此项。stream 输出流.返回值表示成功失败

其他

1.添加水印:

    public Bitmap createWaterBitmap(Bitmap srcBitmap,Bitmap waterBitmap){
        if(null = srcBitmap){
            return null;
        }
        
        int w = srcBitmap.getWidth();
        int h = srcBitmap.getHeight();
        int ww = waterBitmap.getWidth();
        int wh = waterBitmap.getHeight();
        
        Bitmap newB = Bitmap.createBitmap(w,h,Bitmap.config.RARGB_8888);
        Canvas canvas = new Canvas(newB);
        canvas.drawBitmap(src,0,0,null);
        canvas.drawBitmap(waterBitmap,w-ww,h-wh,null);
        return newB;         
    }
    

2.Bitmap设置ANTI_ANLIAS_FLAG无效。

onDraw()中是有效的,因为会先清空canvas,再绘制所有内容.

new Canvas()中效果就打折扣。

ANTI_ANLIAS_FlAG 原理是通过混合前景色和背景色来来产生过渡边缘的。

解决思路:

  1. 避免重绘 避免在onDraw(),onMeasure(),onLayout()中多次绘制

  2. 在绘制前清空

canvas.drawColor(Color.TRANSPARENT,PosterDuff.Mode.CLEAR);