赞
踩
Bitmap称为位图,内部结构是像素矩阵排列。它由A、R、G、B通道组成,其中A代表Alpha,R代表Red,G代表Green。我们在开发中,通常把图片转为Bitmap来处理。
一、Bitmap结构类型
Bitmap按照内部结构,分为6种类型Config:ALPHA_8、RGB_565、ARGB_4444、ARGB_8888、RGBA_F16、HARDWARE。常用类型是RGB_565和ARGB_8888,在Android中默认使用ARGB_8888来创建Bitmap。
ALPHA_8:只使用一个Alpha单通道,共占1个字节
RGB_565:RGB通道按5:6:5比例排列,共占2个字节
ARGB_4444:ARGB通道各占4bits,共占2个字节(已弃用)
ARGB_8888:ARGB通道各占8bits,共占4个字节
RGBA_F16:RGBA通道各占16bits,共占8个字节
HARDWARE:适用于只保存在图像内存的情况,Bitmap不可修改
二、创建Bitmap
Android的API提供创建的Bitmap方法包括:匿名共享内存Bitmap、缩放Bitmap、硬件Bitmap、通用Bitmap。
1、匿名共享内存:createAshmemBitmap(),共享内存利于进程间传递
2、缩放:createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)
3、硬件:Bitmap createHardwareBitmap(GraphicBuffer graphicBuffer)
4、通用:createBitmap(int width, int height, Config config)
三、Bitmap拷贝
Bitmap可以拷贝成一个新的Bitmap,也可以拷贝到Buffer中,或者从Buffer拷贝中出来。
1、拷贝新Bitmap:copy(Config config, boolean isMutable),与原Bitmap的像素密度和色彩空间一致
2、像素拷贝到Buffer:copyPixelsToBuffer(Buffer dst),Buffer的像素与原Bitmap保持一致
3、从Buffer拷贝像素:copyPixelsFromBuffer(Buffer src),如果要再次从Buffer读取,需要先调用rewind方法
四、Bitmap压缩
Bitmap支持压缩格式包括:PNG、JPEG、WEBP,可设置压缩质量0—100。整个过程是把Bitmap压缩并输出到指定的OutputStream,以指定格式保存,也就是Bitmap保存为图片到内存里。调用的压缩方法:compress(CompressFormat format, int quality, OutputStream stream)。提供的压缩格式:
- public enum CompressFormat {
- JPEG (0),
- PNG (1),
- WEBP (2);
-
- CompressFormat(int nativeInt) {
- this.nativeInt = nativeInt;
- }
- final int nativeInt;
- }
五、获取Bitmap大小
我们可以获取Bitmap的每行占用字节数、占用的总字节数和分配内存的字节数。
1、每行占用字节数:getRowBytes()
2、占用的总字节数:getByteCount(),通过每行占用字节数*高度得出
3、分配内存字节数:getAllocationByteCount(),如果Bitmap有重配置,可能比getByteCount()的结果更大
六、读写Bitmap的像素数组
如果要修改Bitmap的像素,我们可以把Bitmap的像素数组读出来,然后修改,再重新赋值给Bitmap。前提是Bitmap是mutable可修改的,可以通过isMutable()方法来判断该Bitmap是否支持修改。
1、获取像素点:getPixel(int x, int y)
2、获取像素数组:getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height)
3、修改像素点:setPixel(int x, int y, @ColorInt int color)
4、修改像素数组:setPixels(@ColorInt int[] pixels, int offset, int stride, int x, int y, int width, int height)
七、获取Bitmap
BitmapFactory提供四种方式来获取Bitmap:文件、资源、字节数组、输入流。
1、从文件获取:decodeFile(String pathName, Options opts)
2、从资源获取:decodeResource(Resources res, int id, Options opts)
3、从字节数组获取:decodeByteArray(byte[] data, int offset, int length, Options opts)
4、从输入流获取:decodeStream(InputStream is, Rect outPadding, Options opts)
------------------------------------------------Bitmap进阶与扩展---------------------------------------------------
八、Bitmap与Mat转换
在openCV的图像处理开发中,操作对象是Mat,而我们Android平台是Bitmap。这时候就需要Bitmap与Mat的互相转换了,需要用到Android平台的openCV SDK,使用Utils类提供的方法来转换。
1、Bitmap转Mat:Utils.bitmapToMat(bitmap, mat);
2、Mat转Bitmap:Utils.matToBitmap(mat, newBitmap);
九:孪生兄弟mipmap
mipmap使用纹理映射技术,压缩率比bitmap高一倍,所占内存比bitmap少一半。在Android开发中,Google官方建议Launcher的icon图标保存在mipmap文件夹下。Bitmap类也提供方法判断是否支持mipmap:boolean hasMipMap()。
十、图片解码为Bitmap
在Android API 28以后,提供ImageDecoder类,可以把编码的图片解码为Bitmap或Drawable,其中支持的图片格式有:PNG、JPEG、WEBP、GIF、HEIF。这个类的功能有点类似BitmapFactory,同样是把图片转为Bitmap。然鹅ImageDecoder功能比较强大。除了解码图片,还支持解码图片头信息监听回调、图片剪裁、设置图片分辨率等等。首先看下图片解码操作:
- @TargetApi(28)
- private void decodeImage(){
- ImageDecoder.Source source = ImageDecoder.createSource(getResources(), R.drawable.ferrari);
- try {
- Bitmap bitmap = ImageDecoder.decodeBitmap(source);
- imgDecode.setImageBitmap(bitmap);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
如果要实现解码时,对图片头信息监听,可以使用OnHeaderDecodedListener来回调,其中info包含图片宽和高。操作如下:
- private void decodeImage(){
- ImageDecoder.Source source = ImageDecoder.createSource(getResources(), R.drawable.ferrari);
- try {
- //使用OnHeaderDecodedListener监听解码信息
- Bitmap bitmap = ImageDecoder.decodeBitmap(source, new ImageDecoder.OnHeaderDecodedListener() {
- @Override
- public void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, ImageDecoder.Source source) {
- Log.e("onHeaderDecoded", "width&&height="+info.getSize());
- }
- });
- imgDecode.setImageBitmap(bitmap);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
如果要剪裁,可以在OnHeaderDecodedListener回调中,调用setCrop方法对图片剪裁:
- @TargetApi(28)
- private ImageDecoder.OnHeaderDecodedListener headerDecodedListener = new ImageDecoder.OnHeaderDecodedListener() {
- @Override
- public void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, ImageDecoder.Source source) {
- //剪裁图片
- decoder.setCrop(new Rect(0, 0, 300, 200));
- }
- };
如果要重新设置分辨率,同样地,可以在OnHeaderDecodedListener回调中,调用setTargetSize(int width, int height)或者setTargetSampleSize(int sampleSize)来设置。
十一、加载高清大图
Android SDK提供BitmapRegionDecoder 来加载高清大图,首先调用newInstance(String pathName, boolean isShareable)方法来创建实例对象,然后是调用decodeRegion(Rect rect, Options options)来解码图片的矩形区域,解码出来是一个bitmap对象:
- private void decodeLargeImage(){
- String rootPath = Environment.getExternalStorageDirectory().getPath();
- String filePath = rootPath + File.separator + "large.jpg";
- try {
- //传参可以是图片路径、输入流、字节数组
- BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(filePath, false);
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- options.inSampleSize = 1;//设置加载图片分辨率
- options.inJustDecodeBounds = false;
- Rect rect = new Rect(0, 0, 100, 100);//待解码的矩形区域
- Bitmap bitmap = regionDecoder.decodeRegion(rect, options);
- imgDecode.setImageBitmap(bitmap);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }

左边是原图,右边是加载图片的一部分:

十二、ndk操作Bitmap
Android有提供ndk操作Bitmap的方法,主要用到bitmap.h头文件,另外在Android.mk添加jnigraphics库依赖(如果是cmake编译,在target_link_libraries中添加jnigraphics):
LOCAL_LDLIBS += -ljnigraphics
bitmap.h头文件主要提供三个方法:
- //获取bitmap对应图片的宽、高、格式
- int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info);
- //上锁,从bitmap获取像素数据赋值给addrPtr指针
- int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);
- //解锁
- int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
包含头文件:
- #include <jni.h>
- #include <android/bitmap.h>
在java层传递jobject类型的bitmap到native层,然后获取到像素数组作进一步处理:
- JNIEXPORT jobject JNICALL Java_com_frank_ndk_modifyBitmap
- (JNIEnv *env, jobject obj, jobject bitmap) {
-
- AndroidBitmapInfo bitmapInfo;//bitmap信息:宽、高、格式
- unsigned char* bitmapPtr;//像素数组
- int ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);//获取bitmap信息
- if (ret != ANDROID_BITMAP_RESULT_SUCCESS){
- return NULL;
- }
- AndroidBitmap_lockPixels(env, bitmap, &bitmapPtr);//上锁
- ...//操作像素数组
- AndroidBitmap_unlockPixels(env, bitmap);//解锁
- return bitmap;
- }
十三、使用BitmapShader渲染Bitmap
Android中有提供BitmapShader来把Bitmap渲染成图片,以自定义圆角图片为例,操作步骤包含:创建BitmapShader、计算缩放系数、设置缩放矩阵、渲染圆角图片。在自定义View的onDraw方法调用以上步骤即可,代码如下:
- //利用BitmapShader渲染自定义圆角图片
- private void drawShader(Canvas canvas){
- //使用bitmap创建shader
- BitmapShader shader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
- //计算bitmap宽与高的较小值
- int size = Math.min(mBitmap.getWidth(), mBitmap.getHeight());
- //计算缩放系数
- float scale = mRadius * 2.0f / size;
- mMatrix.setScale(scale, scale);
- //shader设置matrix矩阵
- shader.setLocalMatrix(mMatrix);
- //shader传递给paint
- mPaint.setShader(shader);
- //渲染圆角图片
- canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
- }

十四、使用RenderNode来绘制Bitmap
在Android Q中,新增RenderNode用于构建硬件加速渲染层次布局。每一个RenderNode包含一个DisplayList和一系列属性,它把复杂的布局切分成更小区域,这样局部更新时所花费代价更小。它需要使用RecordingCanvas来绘制,仅支持硬件加速场景,可以使用Canvas.isHardwareAccelerated()来判断是否支持硬件加速 。
1、创建RenderNode,设置待渲染的矩形区域
- RenderNode renderNode = RenderNode.create("myRenderNode");
- renderNode.setLeftTopRightBottom(0, 0, 50, 50); // Set the size to 50x50
- RecordingCanvas canvas = renderNode.startRecording();
- try {
- canvas.drawRect(rect);
- } finally {
- renderNode.endRecording();
- }
2、在View中绘制RenderNode
- protected void onDraw(Canvas canvas) {
- if (canvas instanceof RecordingCanvas) {
- RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
- if (!myRenderNode.hasDisplayList()) {
- updateDisplayList(myRenderNode);
- }
- recordingCanvas.drawRenderNode(myRenderNode);
- }
- }
3、遍历Bitmap来分块渲染
- private void createDisplayList() {
- mRenderNode = RenderNode.create("mRenderNode");
- mRenderNode.setLeftTopRightBottom(0, 0, width, height);
- RecordingCanvas canvas = mRenderNode.startRecording();
- try {
- for (Bitmap b : mBitmaps) {
- canvas.drawBitmap(b, 0.0f, 0.0f, null);
- canvas.translate(0.0f, b.getHeight());
- }
- } finally {
- mRenderNode.endRecording();
- }
- }
4、释放资源
renderNode.discardDisplayList();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。