安卓bitmap与字节数组关系深入研究应用

Posted 小源子2016

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓bitmap与字节数组关系深入研究应用相关的知识,希望对你有一定的参考价值。

在做一些安卓偏底层开发时候,不得不面对图像数据和字节数组转换工作,但是往往理解不深,产生比较多的疑惑,今天专门做了一些实验、越多一些资料汇总

研究一:理解-1是怎么转换为FF颜色值的

注意:负数在计算中以原码的补码形式表达,例如-1对应的二进制是11111111111111111111111111

①: byte color=-1,a的取值范围是-128~127

②: -1转换为二进制表示 String b = Integer.toBinaryString(color);输出111..111(32个)

③: 转换为16进制字符串表示(注意不应理解为16进制值,只是每个字节转换为16进制表达而已,FFFFFFFF 16进制值实际对应了一个非常大的正常,不要混淆) String b =Integer.toHexString(color);输FFFFFFFF,这是二进制的16进制表达形式,不要理解为16进值了

④:byte颜色值转换为16进制 Integer.toHexString(color & 0xff),与0xff,消除了前面的24位,这样

11111111就对应16进制的FF,10进制的255(-1对应了颜色值的FF,也就是255)

研究二:ARGB_8888、ARGB_4444、 RGB_565、 ALPHA_8概念理解

最后8888表示每个像素的单通道占用了8位,想必4444,占用的位数越多,表达的越清晰

研究三:ARGB_8888顺序(Google给安卓兄弟留的坑)

通过实践,我们可以得知,ARGB_8888 格式图片的各通道顺序其实不是 ARGB,而是 RGBA

    public static Bitmap getBitmap()
        String TAG = "TTTTTTTTTT";
        Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
        bitmap.eraseColor(0xff336699); // AARRGGBB
//        byte[] bytes = new byte[bitmap.getWidth() * bitmap.getHeight() * 4];
//        Buffer dst = ByteBuffer.wrap(bytes);

        ByteBuffer buffer = ByteBuffer.allocate(bitmap.getWidth() * bitmap.getHeight() * 4); //  使用allocate()静态方法创建字节缓冲区
        bitmap.copyPixelsToBuffer(buffer); // 将位图的像素复制到指定的缓冲区
        byte[] bytes = buffer.array();

        // ARGB_8888 真实的存储顺序是 R-G-B-A
        Log.d(TAG, "R: " + Integer.toHexString(bytes[0] & 0xff));
        Log.d(TAG, "G: " + Integer.toHexString(bytes[1] & 0xff));
        Log.d(TAG, "B: " + Integer.toHexString(bytes[2] & 0xff));
        Log.d(TAG, "A: " + Integer.toHexString(bytes[3] & 0xff));
        return bitmap;

    
    
TTTTTTTTTT: R: 33
 TTTTTTTTTT: G: 66
TTTTTTTTTT: B: 99
 TTTTTTTTTT: A: ff

研究四:bitmap和字节的转换

A.主要看下ARGB_8888格式的bitmap转字节(平时这种格式差不多足够了)

//        byte[] bytes = new byte[bitmap.getWidth() * bitmap.getHeight() * 4];
//        Buffer dst = ByteBuffer.wrap(bytes);
        ByteBuffer buffer = ByteBuffer.allocate(bitmap.getWidth() * bitmap.getHeight() * 4); //  使用allocate()静态方法创建字节缓冲区
        bitmap.copyPixelsToBuffer(buffer); // 将位图的像素复制到指定的缓冲区
        byte[] bytes = buffer.array();

a.对于ARGB_8888,应该申请的字节缓存区大小是ByteBuffer.allocate(w*h*4)

b.对于ARGB_4444,经过实验和阅读资料,可能默认被当作ARGB_8888处理了

c.RGB_565 w*h*2大小就足够了,但是怎么从字节数组中转换会16进色值未研究,因为R只占用5bit

经过实验,搞成w*h*3也是不行的,看样子可能需要解析5bit,6bit,5bit这样操作

B.字节转换为Bitmap

android bitmap与byte转换_wh8876的博客-CSDN博客_android bitmap转byte

注意:字节实际内容和ARGB_8888要一致


public static Bitmap createBitmapByByte()
    String TAG = "TTTTTTTTTT";
    Bitmap bitmap= Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
    // 缓存区是R、G、B、A存储顺序
    // 根据之前的研究 byte -1就是代表FF颜色,3代表03色值,
    //这里存储的就是R=FF G=03 B=FF A=FF
    bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(new byte[]-1, 3, -1, -1));

    // 验证bitmap转换为字节,再转换为16进制颜色形式
    ByteBuffer buffer = ByteBuffer.allocate(bitmap.getWidth() * bitmap.getHeight() * 4); //  使用allocate()静态方法创建字节缓冲区
    bitmap.copyPixelsToBuffer(buffer); // 将位图的像素复制到指定的缓冲区
    byte[] bytes = buffer.array();

    ByteBuffer buffer2 = ByteBuffer.allocate(bitmap.getWidth() * bitmap.getHeight() * 4);
    byte[] bytes2 = buffer2.array();
    System.arraycopy(bytes, 0, bytes2, 0, buffer2.array().length);


    // ARGB_8888 真实的存储顺序是 R-G-B-A
    Log.d(TAG, "R: " + Integer.toHexString(bytes2[0] & 0xff));
    Log.d(TAG, "G: " + Integer.toHexString(bytes2[1] & 0xff));
    Log.d(TAG, "B: " + Integer.toHexString(bytes2[2] & 0xff));
    Log.d(TAG, "A: " + Integer.toHexString(bytes2[3] & 0xff));
    return bitmap;


// 打印结果,和预测的一样,说明理解正确
D/TTTTTTTTTT: R: ff
D/TTTTTTTTTT: G: 3
D/TTTTTTTTTT: B: ff
D/TTTTTTTTTT: A: ff

研究五:字节数组的拷贝

先申请一个字节数组,再通过arraycopy方法拷贝过去

ByteBuffer buffer2 = ByteBuffer.allocate(bitmap.getWidth() * bitmap.getHeight() * 4); byte[] bytes2 = buffer2.array(); System.arraycopy(bytes, 0, bytes2, 0, buffer2.array().length);

研究六:修改bitmap的大小

fun imageScale(bitmap: Bitmap, dst_w: Int, dst_h: Int): Bitmap? 
    val src_w = bitmap.width
    val src_h = bitmap.height
    val scale_w = dst_w.toFloat() / src_w
    val scale_h = dst_h.toFloat() / src_h
    val matrix = Matrix()
    matrix.postScale(scale_w, scale_h)
    return Bitmap.createBitmap(
        bitmap, 0, 0, src_w, src_h, matrix,
        true
    )

研究七:相机回调字节数组的格式

public void onPreviewFrame(byte[] data, Camera camera)

理解: NV21==YCbCr_420_SP 属于YUV420sp范围

官方注释:

Camera.Parameters.setPreviewFormat(int) is never called, the default will be the YCbCr_420_SP (NV21) format.

默认是 YCbCr_420_SP 格式,也就是NV21,不过可以通过PreviewFormat修改格式

YUV420sp分为NV12和NV21

 参考资料:

1.你真的知道 ARGB_8888 格式图片的 A、R、G、B 每个通道的排列顺序吗?

你真的知道 ARGB_8888 格式图片的 A、R、G、B 每个通道的排列顺序吗?_阿飞__的博客-CSDN博客_argb中的a

2.四种色彩模式ARGB_8888、ARGB_4444、 RGB_565、 ALPHA_8的区别

四种色彩模式ARGB_8888、ARGB_4444、 RGB_565、 ALPHA_8的区别_hust_twj的博客-CSDN博客_色8888

3.负数的二进制表示方法

负数的二进制表示方法_weixin_33695082的博客-CSDN博客

4.YUV 420、YCbCr 422、RGB 444以及色度二次采样_SkyNullCode的博客-CSDN博客_ycbcr422和444哪个好

5.Camera相机研发介绍_mobileai的博客-CSDN博客_相机开发

以上是关于安卓bitmap与字节数组关系深入研究应用的主要内容,如果未能解决你的问题,请参考以下文章

安卓bitmap与字节数组关系深入研究应用

安卓编程 怎么把一个bitmap从一个activity传到另一个activity里?如果不能穿bi

深入研究数组与伪数组

Java中单字节Ascii的byte字节数组与String转换

将整个位图数据复制到字节数组

ImageView 坐标与 Bitmap Pixels 的对应关系 - Android