Android开发中Bitmap的全面总结

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();

 

  • 10
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Android,可以使用多种方式来读取图片并生成Bitmap对象。以下是几种常见的方法: 1. 使用文件流方式读取图片: ``` FileInputStream fis = new FileInputStream("/sdcard/test.png"); Bitmap bitmap = BitmapFactory.decodeStream(fis); ``` 2. 使用R文件方式读取图片: ``` Bitmap bitmap = BitmapFactory.decodeResource(this.getContext().getResources(), R.drawable.test); ``` 3. 使用ResourceStream方式读取图片,但不使用R文件: ``` Bitmap bitmap = BitmapFactory.decodeStream(getClass().getResourceAsStream("/res/drawable/test.png")); ``` 在读取图片时,还可以通过设置BitmapFactory.Options来进行一些优化操作。例如,可以设置inSampleSize来减小图片的宽高,从而减少内存占用: ``` BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; // 图片宽高都为原来的二分之一,即图片为原来的四分之一 Bitmap bitmap = BitmapFactory.decodeStream(fis, null, options); ``` 需要注意的是,以上方法的路径或资源ID需要根据实际情况进行修改。此外,在使用ImageView显示Bitmap时,可以通过开启视图缓存的方式来获取缓存的Bitmap对象: ``` imageView.setDrawingCacheEnabled(true); Bitmap bitmap = Bitmap.createBitmap(imageView.getDrawingCache()); imageView.setDrawingCacheEnabled(false); ``` 最后,根据需要对获取到的Bitmap对象进行进一步的处理,例如模糊处理等。 #### 引用[.reference_title] - *1* [安卓Bitmap读取图片](https://blog.csdn.net/zbuger/article/details/46895335)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Android从ImageView取出图片bitmap注意事项](https://blog.csdn.net/u013933272/article/details/50987092)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Android学习 | 10.使用位图工具Bitmap在存储卡上读写图片文件](https://blog.csdn.net/M_Nobody/article/details/126141636)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐福记456

您的鼓励和肯定是我创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值