Android bitmap保姆级教学

news/2024/10/18 1:33:46/

1. 认识Bitmap

Bitmap是一个final类,因此不能被继承。Bitmap只有一个构造方法,且该构造方法是没有任何访问权限修饰符修饰,也就是说该构造方法是friendly,但是谷歌称Bitmap的构造方法是private(私有的),感觉有点不严谨。不管怎样,一般情况下,我们不能通过构造方法直接新建一个Bitmap对象。
Bitmap是Android系统中的图像处理中最重要类之一。Bitmap可以获取图像文件信息,对图像进行剪切、旋转、缩放,压缩等操作,并可以以指定格式保存图像文件。

2. 创建Bitmap对象

既然不能直接通过构造方法创建Bitmap,那怎样才能创建Bitmap对象。通常我们可以利用Bitmap的静态方法createBitmap()和BitmapFactory的decode系列静态方法创建Bitmap对象。

  • Bitmap的静态方法createBitmap()
    createBitmap

  • BitmapFactory的decode系列静态方法
    bitmapFactory

2.1. 流——BitmapFactory.decodeStream()

(1) 文件流

FileInputStream fis = new FileInputStream("/sdcard/test.png"); 
Bitmap bitmap=BitmapFactory.decodeStream(fis);

(2) 以ResourceStream的方式代替R文件

Bitmap bitmap=BitmapFactory.decodeStream(getClass().getResourceAsStream(/res/drawable/test.png”));

(3) 以文件流+ R文件 的方式

InputStream in = getResources().openRawResource(R.drawable. test );
Bitmap bitmap = BitmapFactory.decodeStream(in);

(4) 图片放在src目录下

String path = "com/xiangmu/test.png"; //图片存放的路径
InputStream in = getClassLoader().getResourceAsStream(path); //得到图片流
Bitmap imageBitmap3 = BitmapFactory.decodeStream(in);

(5) 图片放在 Assets目录

InputStream in = getResources().getAssets().open(fileName);
Bitmap imageBitmap4 = BitmapFactory.decodeStream(in);

2.2. 文件路径——BitmapFactory.decodeFile()

Bitmap imageBitmap = BitmapFactory.decodeFile(path);// (path 是图片的路径,跟目录是/sdcard)

支持的文件格式:

2.3. 资源ID——BitmapFactory.decodeResource()

(1) 以R文件的方式

// 假设 res/drawable下有 test.jpg文件
Bitmap bitmap =BitmapFactory.decodeResource(getResources(), R.drawable.test);

(2) 获取制定packageName的资源文件

ApplicationInfo appInfo = getApplicationInfo();
//得到该图片的id(name 是该图片的名字,"drawable" 是该图片存放的目录,appInfo.packageName是应用程序的包)
int resID = getResources().getIdentifier(fileName, "drawable", appInfo.packageName);
Bitmap imageBitmap2 = BitmapFactory.decodeResource(getResources(), resID);

2.4. 字节数组——BitmapFactory.decodeByteArray()

(1) bmp格式的数组

byte[] bmpBytes = readFileToByteArray(bmpPath);// bmp文件格式
Bitmap bitmap = BitmapFactory.decodeByteArray(bmpBytes, 0, bmpBytes.length);

(2) yuv-nv21/jpg格式的数组

//data可以是jpg格式的数组
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
//yuv格式的byte[]转换成bitmap
public static Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {   Bitmap bitmap = null;try {YuvImage image = new YuvImage(nv21, ImageFormat.NV21, width, height, null);ByteArrayOutputStream stream = new ByteArrayOutputStream();image.compressToJpeg(new Rect(0, 0, width, height), 80, stream);bitmap = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());stream.close();} catch (IOException e) {e.printStackTrace();}return bitmap;
}

2.5. 从bitmapDrawable获取——bitmapDrawable.getBitmap()

InputStream in = getResources().openRawResource(R.drawable. test );
BitmapDrawable bitmapDrawable = new BitmapDrawable(in);
Bitmap bitmap = bitmapDrawable.getBitmap();注意: openRawResource可以打开 drawable, sound, 和raw资源,但不能是string和color。

2.6. Bitmap.createBitmap()

(1) RGB888格式的数组

//24位RGB转Bitmap
public static Bitmap rgb888ToBitmap(byte[] data, int width, int height)
{byte[] Bits = new byte[data.length * 4]; //RGBA 数组// data.length / 3 表示 3位为一组for (int i = 0; i < data.length / 3; i++){// 原理:24位是有彩色的,所以要复制3位,最后一位Alpha = 0xff;Bits[i * 4] = data[i * 3];Bits[i * 4 + 1] = data[i * 3 + 1];Bits[i * 4 + 2] = data[i * 3 + 2];Bits[i * 4 + 3] = -1;}// Bitmap.Config.ARGB_8888 表示:图像模式为8位Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);bmp.copyPixelsFromBuffer(ByteBuffer.wrap(Bits));return bmp;
}//先转成int再转成bitmap,会稍微快几个ms
public static Bitmap rgb888ToBitmap(byte[] data, int width, int height){int[] iData = new int[data.length/3]; //RGBA 数组// data.length / 3 表示 3位为一组for (int i = 0; i < data.length / 3; i++){// 原理:24位是有彩色的,所以要复制3位,最后一位Alpha = 0xff;iData[i] = (((int)data[i*3]) << 16) + (((int)data[i*3+1]) << 8) + data[i*3+2] + 0xff000000;}// Bitmap.Config.ARGB_8888 表示:图像模式为8位return Bitmap.createBitmap(iData, width, height, Bitmap.Config.ARGB_8888);}

(2) 8位灰度数组

public static Bitmap byteToBitmap(byte[] values, int width, int height) {    //使用8位来保存图片Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);int[] pixels = new int[width * height];for (int i = 0; i < values.length; i++) {//关键代码,生成灰度图,灰度图的argb格式就是0xff开头,后面rgb数值全一样,0xff131313pixels[i] = values[i] * 256 * 256 + values[i] * 256 + values[i] + 0xFF000000;}bitmap.setPixels(pixels, 0, width, 0, 0, width, height);return bitmap;
}//方式二:
public static Bitmap byteToBitmap(byte[] data, int width, int height) {byte[] Bits = new byte[data.length * 4]; //RGBA 数组// 原理:4个字节表示一个灰度,则RGB  = 灰度值,最后一个Alpha = 0xff;for (int i = 0; i < data.length; i++) {		Bits[i * 4] = Bits[i * 4 + 1] = Bits[i * 4 + 2] = data[i];Bits[i * 4 + 3] = -1; //0xff}// Bitmap.Config.ARGB_8888 表示:图像模式为8位Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);bmp.copyPixelsFromBuffer(ByteBuffer.wrap(Bits));return bmp;
}

2.7. Bitmap资源回收

if (bitmap!=null&&!bitmap.isRecycle()) {bitmap.recycle();  bitmap= null;system.gc(); 
}

3. Bitmap的颜色配置信息与压缩方式信息

Bitmap中有两个内部枚举类:ConfigCompressFormatConfig是用来设置颜色配置信息的,CompressFormat是用来设置压缩方式的。

config&compressFormat

  • Config
    

    解析:

    • Bitmap.Config.ALPHA_8:颜色信息只由透明度组成,占8位。
    • Bitmap.Config.ARGB_4444:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占4位,总共占16位。
    • Bitmap.Config.ARGB_8888:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占8位,总共占32位。是Bitmap默认的颜色配置信息,也是最占空间的一种配置。
    • Bitmap.Config.RGB_565:颜色信息由R(Red),G(Green),B(Blue)三部分组成,R占5位,G占6位,B占5位,总共占16位。

通常我们优化Bitmap时,当需要做性能优化或者防止OOM(Out Of Memory),我们通常会使用Bitmap.Config.RGB_565这个配置,因为Bitmap.Config.ALPHA_8只有透明度,显示一般图片没有意义,Bitmap.Config.ARGB_4444显示图片不清楚,Bitmap.Config.ARGB_8888占用内存最多。

  • CompressFormat
    

    解析:

    • Bitmap.CompressFormat.JPEG:表示以JPEG压缩算法进行图像压缩,压缩后的格式可以是".jpg"或者".jpeg",是一种有损压缩。
    • Bitmap.CompressFormat.PNG:表示以PNG压缩算法进行图像压缩,压缩后的格式可以是".png",是一种无损压缩。
    • Bitmap.CompressFormat.WEBP:表示以WebP压缩算法进行图像压缩,压缩后的格式可以是".webp",是一种有损压缩,质量相同的情况下,WebP格式图像的体积要比JPEG格式图像小40%。美中不足的是,WebP格式图像的编码时间“比JPEG格式图像长8倍”。

4. Bitmap对图像进行操作

4.1. Bitmap裁剪图像

Bitmap裁剪图像有两种方式:

  • Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height)

根据源Bitmap对象source,创建出source对象裁剪后的图像的Bitmap。x,y分别代表裁剪时,x轴和y轴的第一个像素,width,height分别表示裁剪后的图像的宽度和高度。
注意:x+width要小于等于source的宽度,y+height要小于等于source的高度。

  • Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)

这个方法只比上面的方法多了mfilter这两个参数,m是一个Matrix(矩阵)对象,可以进行缩放,旋转,移动等动作,filter为true时表示source会被过滤,仅仅当m操作不仅包含移动操作,还包含别的操作时才适用。其实上面的方法本质上就是调用这个方法而已。

public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {return createBitmap(source, x, y, width, height, null, false);
}  

4.2. Bitmap缩放,旋转,移动图像

Bitmap缩放,旋转,移动,倾斜图像其实就是通过Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)方法实现的,只是在实现这些功能的同时还可以实现图像的裁剪。

// 定义矩阵对象  
Matrix matrix = new Matrix();  
// 缩放图像  
matrix.postScale(0.8f, 0.9f);  
// 按中心点镜像垂直翻转
matrix.postScale(1, -1, x/2, y/2);  
// 按中心点镜像水平翻转
matrix.postScale(-1, 1, x/2, y/2);
// 向左旋转(逆时针旋转)45度,参数为正则向右旋转(顺时针旋转) 
matrix.postRotate(-45);  
// 按中心点顺时针旋转90度 
matrix.postRotate(90, x/2, y/2); 
//移动图像
//matrix.postTranslate(100,80);
Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);    

Matrix的postScalepostRotate方法还有多带两个参数的重载方法postScale(float sx, float sy, float px, float py)postRotate(float degrees, float px, float py),后两个参数pxpy都表示以该点为中心进行操作。
注意:虽然Matrix还可以调用postSkew方法进行倾斜操作,但是却不可以在此时创建Bitmap时使用。

4.3. Bitmap保存图像与释放资源

bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.feng);
File file=new File(getFilesDir(),"lavor.jpg");
if(file.exists()){file.delete();
}
try {FileOutputStream outputStream=new FileOutputStream(file);bitmap.compress(Bitmap.CompressFormat.JPEG,90,outputStream);outputStream.flush();outputStream.close();
} catch (FileNotFoundException e) {e.printStackTrace();
} catch (IOException e) {e.printStackTrace();
}
bitmap.recycle();//释放bitmap的资源,这是一个不可逆转的操作

4.4. Bitmap获取rgb格式数组

//从bitmap中获取rgb格式的数组
byte[] getByteFromBitmap(Bitmap bitmap){int width = bitmap.getWidth();int height = bitmap.getHeight();int[] pixels = new int[width * height];bitmap.getPixels(pixels, 0, width, 0, 0, width, height);byte[] pixelsBytes = getRgbFromPixels(pixels);bitmap.recycle();return pixelsBytes;
}
byte[] getRgbFromPixels(int[] vArr) {byte[] bytes = new byte[vArr.length*3];for (int i = 0;i<vArr.length;i++){int accum = vArr[i];bytes[i*3] = (byte)((accum>>16) & 0xFF);bytes[i*3+1] = (byte)((accum>>8) & 0xFF);bytes[i*3+2] = (byte)(accum & 0xFF);
//      bytes[i*4+3] = (byte)((accum>>24) & 0xFF);}return bytes;
}

5. BitmapFactory通过BitmapFactory.Options对图像进行操作

BitmapFactory是通过BitmapFactory.Options对图像进行操作的,然后将操作后的图像生成Bitmap对象或者将操作后的图像用已经存在的Bitmap保存,当不能用之保存时会返回null
BitmapFactory.Options中常用的字段有:
options

  • inBitmap:如果设置将会将生成的图像内容加载到该Bitmap对象中。
  • inDensity:给Bitmap对象设置的密度,如果inScaled为true(这是默认的),而若inDensityinTargetDensity不匹配,那么就会在Bitmap对象返回前将其缩放到匹配inTargetDensity
  • inDither:是否对图像进行抖动处理,默认值是false。
  • inJustDecodeBounds:如果设置成true,表示获取Bitmap对象信息,但是不将其像素加载到内存,不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM。设置为false,BitmapFactory返回bitmap。
  • inMutable:如果设为true,将返回一个mutable的bitmap,可用于修改BitmapFactory加载而来的bitmap。
  • inPreferredConfig:Bitmap对象颜色配置信息,默认是Bitmap.Config.ARGB_8888
    • ALPHA_8:每个像素用占8位,存储的是图像的透明值,占1个字节;
    • RGB_565:每个像素用占16位,分别为5-R,6-G,5-B通道,占2个字节;
    • ARGB-4444:每个像素占16位,即每个通道用4位表示,占2个字节;
    • ARGB_8888:每个像素占32位,每个通道用8位表示,占4个字节。
  • inSampleSize:获取采样率,设置的值大于1时,图像高、宽分别以2的inSampleSize次方分之一缩小;
    设置的值小于等于1时,图像高、宽不变。用于对图像进行压缩,设置的值为2的整数次幂或者接近2的整数次幂,当次设置为2时,宽和高为都原来的1/2,图像所占空间为原来的1/4。
  • inScaled:设置是否缩放位图。
  • inTargetDensity:绘制到目标Bitmap上的密度。
  • outHeight:Bitmap对象的高度。
  • outWidth:Bitmap对象的宽度。

BitmapFactory.Options的作用:
(1).防止内存溢出;
(2).节省内存开销;
(3).系统更流畅;

6. 使用Bitmap时防止OOM的有效方法

6.1. 高效压缩图片

/*** 谷歌推荐使用方法,从资源中加载图像,并高效压缩,有效降低OOM的概率* @param res 资源* @param resId 图像资源的资源id* @param reqWidth 要求图像压缩后的宽度* @param reqHeight 要求图像压缩后的高度* @return*/
public Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {// 设置inJustDecodeBounds = true ,表示获取图像信息,但是不将图像的像素加入内存final BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(res, resId, options);// 调用方法计算合适的 inSampleSizeoptions.inSampleSize = calculateInSampleSize(options, reqWidth,reqHeight);// inJustDecodeBounds 置为 false 真正开始加载图片options.inJustDecodeBounds = false;//将options.inPreferredConfig改成Bitmap.Config.RGB_565,// 是默认情况Bitmap.Config.ARGB_8888占用内存的一般options.inPreferredConfig= Bitmap.Config.RGB_565;return BitmapFactory.decodeResource(res, resId, options);
}// 计算 BitmapFactpry 的 inSimpleSize的值的方法
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {if (reqWidth == 0 || reqHeight == 0) {return 1;}// 获取图片原生的宽和高final int height = options.outHeight;final int width = options.outWidth;Log.d(TAG, "origin, w= " + width + " h=" + height);int inSampleSize = 1;// 如果原生的宽高大于请求的宽高,那么将原生的宽和高都置为原来的一半if (height > reqHeight || width > reqWidth) {final int halfHeight = height / 2;final int halfWidth = width / 2;// 主要计算逻辑// Calculate the largest inSampleSize value that is a power of 2 and// keeps both// height and width larger than the requested height and width.while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {inSampleSize *= 2;}}Log.d(TAG, "sampleSize:" + inSampleSize);return inSampleSize;
}  

6.2. 使用缓存

常用的缓存有内存缓存LruCache和磁盘缓存DiskLruCache


http://www.ppmy.cn/news/107283.html

相关文章

通过 Wacom 的 Project Mercury 提高远程办公效率

过去几年中&#xff0c;我们的工作方式发生了翻天覆地的变化。疫情加快了对远程办公和协作的采纳&#xff0c;导致人们更加依赖技术来联系团队和提高工作效率。 但是&#xff0c;那些依靠专门硬件和软件来完成工作的创作者呢&#xff1f;艺术家、设计师和开发人员需要使用专门…

基于matlab使用差分波束成形技术形成线性差分麦克风阵列

一、前言 本示例展示了差分波束成形的基本概念&#xff0c;以及如何使用该技术形成线性差分麦克风阵列。 二、加法与差分麦克风阵列 麦克风阵列已部署在许多音频应用中。根据布局的不同&#xff0c;麦克风阵列可分为两大类&#xff1a;加法麦克风阵列和差分麦克风阵列。附加麦克…

ESXI7.0安装Windows Server 2008 R2

1&#xff1a;使用VC正常建立虚拟机&#xff0c;前四项根据自己的时间情况选择&#xff0c;兼容性用默认的ESXI7.0U2及更高版本。 2&#xff1a;客户机操作系统选择Windows,客户机操作系统版本选择我们想安装的Windows Server 2008 R2(64位&#xff09; 3&#xff1a;自定义硬件…

【Django】makemigrations 时出现 sqlite3.OperationalError: no such column: XXX

models文件修改后执行makemigrations报错&#xff1a; sqlite3.OperationalError: no such column: XXX但是数据库没问题&#xff0c;migrations文件也没问题 查看一下views等代码中有没有直接调model类的地方&#xff0c;如果有的话给注掉就好了 问题的原因是在初始化时候也…

STM32单片机语音识别家庭灯光控制系统

实践制作DIY- GC0132-语音识别家庭灯光控制系统 一、功能说明&#xff1a; 基于STM32单片机设计-语音识别家庭灯光控制系统 二、功能介绍&#xff1a; STM32F103C系列最小系统板语音识别模块18650锂电池太阳能充电板LCD1602显示器4个LED灯板对应卧室、厨房、客厅、厕所…

Python编程语言简介

Python 是荷兰人 Guido van Rossum &#xff08;吉多范罗苏姆&#xff0c;中国程序员称其为“龟叔”&#xff09;在 1990 年初开发的一种解释型编程语言。 Python 的诞生是极具戏曲性的&#xff0c;据 Guido 自述记载&#xff0c;Python 语言是在圣诞节期间为了打发无聊的时间…

Docker环境java程序的时间设置

先上解决方案 java程序生成的时间、日志时间不对的解决方案&#xff1a; #在Dockerfile文件中加入以下两行代码&#xff0c;用于指定bild的镜像为东八区 ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezoneDoc…

Benewake(北醒) TFmini-i-485/TF02-i-485/TF03-485 雷达Modbus协议在Python Tkinter模块上实现功能配置的GUI设计

目录 实验目的测试环境Python库需求Benewake(北醒) TF雷达接线示意图库安装说明例程运行展示 实验目的 实现485接口系列雷达Modbus协议在Python下Tkinter模块实现功能配置的GUI设计。 本例程主要功能如下&#xff1a; 1.设备连接&#xff08;已知雷达设备的波特率和站号&#…