Android之 Bitmap使用

news2024/11/15 12:14:25

一,简介

1.1 Bitmap是一种图片在内存中的表现形式,不管是png,还是jpg最终都是以bitmap的形式显示到控件上面。

Bitmap是一种位图,位图​是点阵图像​或栅格图像,是由称作像素(图片元素)的单个点组成的。这些点可以进行不同的排列和染色以构成图样。当放大位图时,可以看见赖以构成整个图像的无数单个方块

二 常见的图片格式

JPEG是一种有损压缩格式,不支持透明度,进行压缩时需要选择适当的压缩率,避免图片质量太差
PNG是一种无损压缩格式,支持所有颜色,包括透明度,适合质量清晰度非常高的图片
WEBP支持无损压缩和有损压缩,无损压缩率优于PNG,有损压缩率优于JPEG,也支持所有颜色,但唯一的缺点是压缩效率不如JPEG和PNG

1.3 位深,即色深指的是每一个像素点用多少bit来存储ARGB值,色深越大,图片的色彩越丰富

ARGB_8888:四个通道都是8位,每个像素占用4个字节,图片质量是最高的,但是占用的内存也是最大的;

ARGB_4444:四个通道都是4位,每个像素占用2个字节,图片的失真比较严重;

RGB_565:没有A通道,每个像素占用2个字节,图片失真小,但是没有透明度;

ALPHA_8:只有A通道,每个像素占用1个字节大大小,只有透明度,没有颜色值

三 Bitmap内存的占用计算

原生api计算

  • 在API12开始提供了getByteCount()方法,用来计算Bitmap所占的内存。
  • 在API19开始getAllocationByteCount()方法代替了getByteCount()。

区别

  • 一般情况下两者是相等的。
  • 在Bitmap复用的情况下,getByteCount()表示新解码图片所占内存大小(并非实际大小,实际大小是复用的那个Bitmap的大小),而getAllocationByteCount()则表示被复用Bitmap所占的内存大小。

从磁盘加载图片内存计算:

图片的长度 * 图片的宽度 * 一个像素点占用的字节数

如果从资源文件夹中加载:

Bitmap内存占用 ≈ 像素数据总大小 = 图片宽 × 图片高× (当前设备密度dpi/图片所在文件夹对应的密度dpi)^2 × 每个像素的字节大小

加载规则:

同一张图片放在不同的目录下,分辨率会发生变化
图片不在资源目录中(如drawable中),其使用的默认dpi为160
当设备的像素密度和资源文件夹的像素密度相同时,加载图片时不发生缩放 

四 Bitmap加载内存的优化方案

方案一:编码

采用低编码来加载bitmap,即用RGB_565或ARGB_4444占用内存低的编码方式来加载

Bitmap originBitmap=BitmapFactory.decodeResource(getResources(),R.mipmap.reuse);
iv_origin.setImageBitmap(originBitmap);
Log.e(TAG,"原图大小:"+originBitmap.getAllocationByteCount());
BitmapFactory.Options options=new BitmapFactory.Options();
//一般通过修改Bitmap的编码格式为RGB_565来达到压缩目的时,不建议修改为ARGB_4444,图片失真严重
options.inPreferredConfig=Bitmap.Config.RGB_565;
Bitmap compressBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.reuse, options);
Log.e(TAG,"压缩后大小:"+compressBitmap.getAllocationByteCount());
iv_reused.setImageBitmap(compressBitmap);

方案二:采样

修改BitmapFactory.Options.inSampleSize可以修改图片的宽高,来达到修改图片的占用内存

BitmapFactory.Options options = new BitmapFactory.Options();
//不获取图片,不加载到内存中,只返回图片属性
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(photoPath, options);
//图片的宽高
int outHeight = options.outHeight;
int outWidth = options.outWidth;
Log.d("mmm", "图片宽=" + outWidth + "图片高=" + outHeight);
//计算采样率
int i = utils.computeSampleSize(options, -1, 1000 * 1000);
//设置采样率,不能小于1 假如是2 则宽为之前的1/2,高为之前的1/2,一共缩小1/4 一次类推
options.inSampleSize = i;
Log.d("mmm", "采样率为=" + i);
//图片格式压缩
//options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(photoPath, options);
float bitmapsize = getBitmapsize(bitmap);
Log.d("mmm","压缩后:图片占内存大小" + bitmapsize + "MB / 宽度=" + bitmap.getWidth() + "高度=" + bitmap.getHeight());

 方案三:复用

Bitmap的复用就需要用到BitmapFactory.Options.inBitmap属性

作用:

  • 不使用这个属性,你加载三张图片,系统会给你分配三份内存空间,用于分别储存这三张图片。
  • 如果用了inBitmap这个属性,加载三张图片,这三张图片会指向同一块内存,而不用开辟三块内存空间。

限制 

  • 3.0-4.3 复用的图片大小必须相同, 编码必须相同
  • 4.4以上 复用的空间大于等于即可, 编码不必相同
  • 其它限制,不支持WebP,图片复用,这个属性必须设置为true; options.inMutable = true;

实例: 

public void reuseBitmap(View view) {
        BitmapFactory.Options  options=new BitmapFactory.Options();
        options.inMutable=true;
        options.inDensity=320;
        options.inTargetDensity=320;
        Bitmap  origin= BitmapFactory.decodeResource(getResources(),R.mipmap.origin,options);
        iv_origin.setImageBitmap(origin);
        Log.e(TAG,origin.toString());
        Log.e(TAG,"origin:getByteCount:"+origin.getByteCount()+",origin:getAllocationByteCount:"+origin.getAllocationByteCount());
        
        options.inDensity=320;
        options.inTargetDensity=160;
        options.inMutable=true;
        options.inBitmap=origin;

        Bitmap reuseBitmap=BitmapFactory.decodeResource(getResources(),R.mipmap.reuse,options);
        iv_reused.setImageBitmap(reuseBitmap);
        Log.e(TAG,reuseBitmap.toString());
        Log.e(TAG,"origin:getByteCount:"+origin.getByteCount()+",origin:getAllocationByteCount:"+origin.getAllocationByteCount());
        Log.e(TAG,"reuseBitmap:getByteCount:"+reuseBitmap.getByteCount()+",reuseBitmap:getAllocationByteCount:"+reuseBitmap.getAllocationByteCount());
}

 方案四:缓存

可以利用LruCache进行内存缓存,LruCache底层使用LinkedHashMap存储数据,并在达到设置的最大内存前将最近最少使用的数据删除,使用LruCache可以避免内存的频繁创建和销毁带来的内存开销。

五 Bitmap存储位置的变化 

  • android2.3.3(API level 10)和更早的版本,bitmap对象和对象里对应的像素数据是分开存储的,bitmap存在虚拟机的堆里,而像素数据存储在native内存里。
  • android3.0(API level 11)到android7.1(API level25),bitmap对象及其像素数据都存储在虚拟机的堆里。
  • android8.0(API level 26)开始,bitmap对象存储在虚拟机的堆里,而对应的像素数据存储在native堆里。

 六 Bitma压缩

方法一:质量压缩

质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的

ByteArrayOutputStream baos = new ByteArrayOutputStream();
int quality = 50;//取值:0-100
bit.compress(Bitmap.CompressFormat.JPEG, quality, baos);
byte[] bytes = baos.toByteArray();
bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

compress(Bitmap.CompressFormat.JPEG, quality, baos)

第一个参数取值是图片的格式:Bitmap.CompressFormat.JPEG、Bitmap.CompressFormat.PNG、Bitmap.CompressFormat.WEBP。注意Bitmap.CompressFormat.XXX要和文件格式一致。
第二个参数:quality代表压缩程度,取值0-100。100表示不压缩,0表示压缩到最小的图片文件大小。Bitmap.CompressFormat.PNG压缩格式不起作用,因为png图片是无损的,不能进行压缩

方法二:采样率压缩

采样率压缩时图片的宽高按比例压缩,压缩后图片像素数会减少 

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
bm = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getAbsolutePath()+ "图片地址", options);

设置inSampleSize的值(int类型)后,假如设为2,则宽和高都为原来的1/2,宽高都减少了,自然内存也降低了 

方法三:缩放法压缩
给定压缩图的宽高,根据原图的宽高,计算宽、高压缩比例,按比例压缩 

Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
bm = Bitmap.createBitmap(bit, 0, 0, bit.getWidth(),
bit.getHeight(), matrix, true);

七 加载高清大图

我们可以用BitmapRegionDecoder这个类,加载图片的一部分区域

//支持传入图片的路径,流和图片修饰符等
BitmapRegionDecoder mDecoder = BitmapRegionDecoder.newInstance(path, false);
//需要显示的区域就有由rect控制,options来控制图片的属性
Bitmap bitmap = mDecoder.decodeRegion(mRect, options);

实例:

InputStream inputStream = getAssets().open("flower.jpg");
 
//获取图片的宽高,但不加载到内存中
BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
tmpOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, tmpOptions);
int mImageWidth = tmpOptions.outWidth;
int mImageHeight = tmpOptions.outHeight;

//获取BitmapRegionDecoder,并设置相关参数
BitmapRegionDecoder mDecoder =      BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options mOptions = new BitmapFactory.Options();
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;

//显示图片
Rect rect = new Rect(mImageWidth / 2 - 400, mImageHeight / 2 - 400,
		mImageWidth / 2 + 400, mImageHeight / 2 + 400);
Bitmap newBitmap = mDecoder.decodeRegion(rect, mOptions);
image_view.setImageBitmap(newBitmap);

八 图片的编辑

8.1 图片的裁剪

可以通过Canvas.drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)

Canvas rootCanvas = new Canvas();
rootCanvas.drawColor(Color.WHITE);
Rect preDst = new Rect(0, 0, 100, 100);
Bitmap bitmap1 = BitmapFactory.decodeFile("图片文件路径");       
rootCanvas.drawBitmap(bitmap1, null, preDst, null);
bitmap1.recycle();

 8.2 图片合成

private Bitmap getJointBitmap(Bitmap splitBitmap) {
    int width = splitBitmap.getWidth();
    int height = splitBitmap.getHeight();
    Bitmap bitmap = Bitmap.createBitmap(
                width * 2, height * 2, Bitmap.Config.RGB_565);
    Canvas canvas = new Canvas(bitmap);
    canvas.drawBitmap(splitBitmap, 0, 0, null);
    canvas.drawBitmap(splitBitmap, width, 0, null);
    canvas.drawBitmap(splitBitmap, 0, height, null);
    canvas.drawBitmap(splitBitmap, width, height, null);
    return bitmap;
} 

8.3 矩阵变换

Bitmap是像素点的集合,我们可以通过矩阵运算改变每个像素点的位置,达到图形变换的效果。Android中可以通过Matrix类来进行变换,Matrix本身是一个3x3的矩阵,可以通过Matrix m = new Matrix()新建一个单位矩阵,原始矩阵的值如下所示

[1   0   0]
[0   1   0]
[0   0   1] 

Matrix中各个位置的变换信息如下所示,scale表示缩放,skew表示错切,trans表示平移,persp等表示透视参数。Bitmap中的每个像素点可以使用一个3x1的矩阵表示,其中x表示当前像素点的横坐标,y表示纵坐标。用该矩阵左乘Bitmap中的所有像素后,就能得到变换后的图像。

[scaleX  skewX   transX]     [x]     [scaleX * x + skewX * y + transX]
[skewY   scaleY  transY]  x  [y]  =  [skewY * x + scaleY * y + transY]
[persp0  persp1  persp2]     [1]     [persp0 * x + persp1 * y + persp2] 
 

Matrix是一个容器,保存了用户期望的矩阵变换信息。在将Matrix应用于Bitmap之前,我们可以对其进行各种操作,将变换信息保存进去。矩阵运算可以实现平移、旋转、缩放、错切,因此Matrix也为提供了类似方法

Matrix matrix = new Matrix();
matrix.setTranslate(float dx,float dy): 控制 Matrix 进行位移。
matrix.setSkew(float kx,float ky): 控制 Matrix 进行倾斜,kx、ky为X、Y方向上的比例。
matrix.setSkew(float kx,float ky,float px,float py): 控制 Matrix 以 px, py 为轴心进行倾斜,kx、ky为X、Y方向上的倾斜比例
matrix.setRotate(float degrees): 控制 Matrix 进行 depress 角度的旋转,轴心为(0,0)
matrix.setRotate(float degrees,float px,float py): 控制 Matrix 进行 depress 角度的旋转,轴心为(px,py)
matrix.setScale(float sx,float sy): 设置 Matrix 进行缩放,sx, sy 为 X, Y方向上的缩放比例。
matrix.setScale(float sx,float sy,float px,float py): 设置 Matrix 以(px,py)为轴心进行缩放,sx、sy 为 X、Y方向上的缩放比例 

8.4 颜色变换

可以通过ColorMatrixColorFilter来进行颜色的改变,包括色相,饱和度,零度。以及滤镜的处理,RGBA通道的改变都是通过改类实现的
Android中的色彩是以ARGB的形式存储的,我们可以通过ColorMatrix修改颜色的值,ColorMatrix定义了一个4x5的float矩阵,矩阵的4行分别表示在RGBA上的向量,其范围值在0f-2f之间,如果为1就是原效果

ColorMatrix colorMatrix = new ColorMatrix(new float[]{
		1, 0, 0, 0, 0,
		0, 1, 0, 0, 0,
		0, 0, 1, 0, 0,
		0, 0, 0, 1, 0,
}); 

ColorMatrix与颜色之间的运算如下所示,其实就是矩阵运算,与上一节的Matrix类似 

[a, b, c, d, e]     [R]     [a*R + b*G + c*B + d*A + e]
[f, g, h, i, j]     [G]     [f*R + g*G + h*B + i*A + j]
[k, l, m, n, o]  x  [B]  =  [k*R + l*G + m*B + n*A + o]
[p, q, r, s, t]     [A]     [p*R + q*G + r*B + s*A + t]
                    [1] 

有了ColorMatrix,就可以对Bitmap上所有的颜色进行修改,例如调整每个通道的值,将初始值1改为0.5,就可以将Bitmap变暗

8.4 图像的混合

图像混合是指对两张原始图像(我们称为DST和SRC)的内容按某种规则合成,从而形成一张包含DST和SRC特点的新图像。例如DST为圆形图像,SRC为照片,可以将它们合成为圆形照片。

Android通过PorterDuffXfermode实现图像混合,它实际上是通过公式对两张图像在Canvas上的所有像素进行ARGB运算,最终在每个像素点得到新的ARGB值。需要注意的是,在onDraw(Canvas)方法中进行图像混合时,先绘制的图像为DST,后绘制的图像为SRC,因此需要注意图像的绘制顺序。

PorterDuffXfermode一共提供了18种混合模式,它们的计算公式如下,Sa表示SRC的ALPHA通道,Sc表示SRC的颜色;Da表示DST的ALPHA通道,Dc表示DST的颜色。以CLEAR为例,该模式会清除SRC区域的所有内容

 示例:

private void drawCompositionInFullSize(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        Bitmap dst = makeDst();
        Bitmap src = makeSrc();
        // 绘制DST
        canvas.drawBitmap(dst, 0, 0, mPaint);
        // 设置图像混合模式
        mPaint.setXfermode(mPorterDuffXfermode);
        // 绘制SRC
        canvas.drawBitmap(src, 0, 0, mPaint);
        // 清除图像混合模式
        mPaint.setXfermode(null);
}

8.5 色彩合成,比如滤镜效果

 色彩合成可以为图片添加新的效果,当使用纯色与照片混合时,可以改变图片整体的色调。例如黄色可以让图片具有泛黄的怀旧效果,红色可以让图片更温暖。以下代码通过SCREEN混合模式将半透明的红色与图片混合

canvas.drawColor(Color.WHITE);
int sc = canvas.saveLayer(0, 0, mWidth, mHeight, null);
canvas.drawColor(0x44FF0000);
mPaint.setXfermode(mMode);
canvas.drawBitmap(mBitmap, null, mRect, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(sc);

 九 Bitmap在android中图片的合成

9.1 Bitmap原图是不能直接修改的,我们可以通过画布创建一个图片的副本,在上面绘制其它元素。如下创建副本

//加载原图
Bitmap bmSrc = BitmapFactory.decodeResource(getResources(), R.drawable.photo3);
//1.创建副本,创建与原图一模一样大小的bitmap对象,该对象中目前是没有内容的,可以比喻为创建了和原图一样大小的白纸
Bitmap bmCopy = Bitmap.createBitmap(bmSrc.getWidth(), bmSrc.getHeight(), bmSrc.getConfig());
//2.创建画笔对象
Paint paint = new Paint();
//3.创建画板,把白纸铺到画板上
Canvas canvas = new Canvas(bmCopy);
//4.在画板上绘制原图
canvas.drawBitmap(bmSrc, new Matrix(), paint);

9.2 单位的转换,真实打印图片可能是固定的厘米,毫米等物理单位,但手机上的是像素的单位,像素随分辨率的大小而改变

经尝试默认保存后的bitmap分辨率只有96dpi,但打印图片可能要求非常高,300或者400dpi,怎样控制保存后的图片符合指定的厘米尺寸和指定的分辨率呢,下面是从合成图片到保存图片的过程实例

厘米或者毫米转指定分辨率的像素尺寸

/**
 * 像素=毫米x分辨率
 * dip,像素/英寸单位,1英寸=2.54厘米=25.4毫米
 * metrics.xdpi * (1.0f/25.4f)  代表分辨率x1.0fx1英寸  就是所需的dip(25.4f毫米级表示1英寸)
 * (300f / 25.4f) 一英寸上有300像素,一毫米上有 (300f / 25.4f)像素
 * value 毫米值
 * 
 * dipValue 分辨率 默认96 ,可以更改为400或者300
 */
public float applyDimension(float value) {
    return value * dipValue * (1f / 25.4f);
}

9.3 合成图片 

   //合成图片190x125  7x10
    private File setCanvasImages_190_125() {
        int widthReal = getApplyDimension(190);
        int hightReal = getApplyDimension(125);
        int marginReal = getApplyDimension(3);
        // 打印机250x180  7x10
        int width = getApplyDimension(190) - marginReal;
        int hight = getApplyDimension(125) - marginReal;
        Bitmap bitmap = Bitmap.createBitmap(width, hight, Bitmap.Config.ARGB_8888);
        Canvas rootCanvas = new Canvas(bitmap);
        rootCanvas.drawColor(Color.WHITE);
        rootCanvas.drawBitmap(bitmap, 0, 0, null);

        //创建压边图
        int rightRealHeight = getApplyDimension(17);
        //顶部边距
        int marginTop = getApplyDimension(0);
        int rightHeight = rightRealHeight + marginTop;
        Bitmap rightBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.print_right_bg_190_125);
        Rect dst = new Rect(0, marginTop, width, rightHeight);
        rootCanvas.drawBitmap(rightBitmap, null, dst, null);

        //订单号
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.WHITE);
        paint.setTextSize(sp2px(context, 18));
        paint.setTextAlign(Paint.Align.LEFT);
        int marOrderRight = getApplyDimension(60);
        int marOrderTop = getApplyDimension(2);
        rootCanvas.drawText(context.orderBean.order_no, width - marOrderRight, rightHeight / 2 + marOrderTop, paint);
        int orderWidth = (int) paint.measureText(context.orderBean.order_no);

        //二维码
        int qrHeight = getApplyDimension(13);
        //起始位置
        int startX = width - marOrderRight + orderWidth;
        //2cm
        int qrWith2cm = getApplyDimension(2);
        //边距
        Rect qrDst = null;
        if (width - startX > rightRealHeight) {//如果空间足够就显示16cm
            int margeWidth = (int) ((width - startX - qrHeight) / 2f);
            qrDst = new Rect(startX + margeWidth, (rightHeight - qrHeight) / 2 + marginTop / 2, startX + qrHeight + margeWidth, qrHeight + ((rightHeight - qrHeight) / 2) + marginTop / 2);
        } else {//空间不足够就计算二维码大小
            //二维码大小
            qrHeight = width - startX - qrWith2cm * 2;
            int margeWidth = (int) ((width - startX - qrHeight) / 2f);
            qrDst = new Rect(startX + margeWidth, (rightHeight - qrHeight) / 2 + marginTop / 2, startX + qrHeight + margeWidth, qrHeight + ((rightHeight - qrHeight) / 2) + marginTop / 2);
        }

        rootCanvas.drawBitmap(qrcodeBitmap, null, qrDst, null);


        //创建预览图
        int pltWidth = (int) Float.parseFloat(context.orderBean.plt_height);
        int pltHeight = (int) Float.parseFloat(context.orderBean.plt_width);
        int preWidth = getApplyDimension(pltWidth);
        int preHeight = getApplyDimension(pltHeight);
        resourceBitmap = adjustPhotoRotation(resourceBitmap, 90);
        resourceBitmap = adjustPhotoRotation(resourceBitmap, 90);
        resourceBitmap = adjustPhotoRotation(resourceBitmap, 90);
        Rect preDst = new Rect((width - preWidth) / 2, rightHeight + (hight - rightHeight - preHeight) / 2, (width - preWidth) / 2 + preWidth, hight - (hight - rightHeight - preHeight) / 2);
        rootCanvas.drawBitmap(resourceBitmap, null, preDst, null);


        //创建边距背景
        int widthBg = widthReal;
        int hightBg = hightReal;
        Bitmap bitmapBg = Bitmap.createBitmap(widthBg, hightBg, Bitmap.Config.ARGB_8888);
        Canvas bgCanvas = new Canvas(bitmapBg);
        bgCanvas.drawColor(Color.WHITE);
        bgCanvas.drawBitmap(bitmapBg, 0, 0, null);
        Rect preDstResult = new Rect((widthBg - width) / 2, (hightBg - hight) / 2, (widthBg - width) / 2 + width, hightBg - (hightBg - hight) / 2);
        bgCanvas.drawBitmap(bitmap, null, preDstResult, null);
        //旋转90度
        bitmapBg = adjustPhotoRotation(bitmapBg, 90);

        File file = saveToImage(bitmapBg);
        resourceBitmap.recycle();
        qrcodeBitmap.recycle();
        bitmap.recycle();
        bitmapBg.recycle();
        rightBitmap.recycle();
        return file;
    }

保存为指定分辨率的图片

 public File saveToImage(Bitmap bitmap) {
        FileOutputStream fos;
        try {
            // SD卡根目录
            File dir = context.getExternalFilesDir("print");
            if (!dir.exists()) {
                dir.mkdirs();
            }
            File picFile = new File(dir, type + "_" + System.currentTimeMillis() + ".jpg");
            fos = new FileOutputStream(picFile);
            ByteArrayOutputStream imageByteArray = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageByteArray);

            byte[] imageData = imageByteArray.toByteArray();

            //300 will be the dpi of the bitmap
            setDpi(imageData, dipValue);

            fos.write(imageData);
            fos.flush();
            fos.close();
            bitmap.recycle();
            return picFile;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    //设置图片分辨率
    public void setDpi(byte[] imageData, int dpi) {
        imageData[13] = 1;
        imageData[14] = (byte) (dpi >> 8);
        imageData[15] = (byte) (dpi & 0xff);
        imageData[16] = (byte) (dpi >> 8);
        imageData[17] = (byte) (dpi & 0xff);
    }

注意:

 图片修改分辨率只对jpg图片起用,png图片是不起用的,因为png是无损的,不能修改png的压缩和分辨率

十 Bitmap常用API

Bitmap类

public void recycle()  // 回收位图占用的内存空间,把位图标记为Dead
public final boolean isRecycled()  //判断位图内存是否已释放
public final int getWidth() //获取位图的宽度
public final int getHeight() //获取位图的高度
public final boolean isMutable() //图片是否可修改
public int getScaledWidth(Canvas canvas) //获取指定密度转换后的图像的宽度
public int getScaledHeight(Canvas canvas) //获取指定密度转换后的图像的高度
public boolean compress(CompressFormat format, int quality, OutputStream stream) //按指定的图片格式以及画质,将图片转换为输出流。
format:压缩图像的格式,如Bitmap.CompressFormat.PNG或Bitmap.CompressFormat.JPEG
quality:画质,0-100.0表示最低画质压缩,100以最高画质压缩。对于PNG等无损格式的图片,会忽略此项设置。
stream: OutputStream中写入压缩数据。
return: 是否成功压缩到指定的流。

public static Bitmap createBitmap(Bitmap src)  //以src为原图生成不可变得新图像
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) //以src为原图,创建新的图像,指定新图像的高宽以及是否可变。
public static Bitmap createBitmap(int width, int height, Config config) //创建指定格式、大小的位图
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) //以source为原图,创建新的图片,指定起始坐标以及新图像的高宽。

 BitmapFactory工厂类

public static Bitmap decodeFile(String pathName, Options opts)  //从文件读取图片
public static Bitmap decodeFile(String pathName)
public static Bitmap decodeStream(InputStream is)  //从输入流读取图片
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
public static Bitmap decodeResource(Resources res, int id)  //从资源文件读取图片
public static Bitmap decodeResource(Resources res, int id, Options opts)
public static Bitmap decodeByteArray(byte[] data, int offset, int length)  //从数组读取图片
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
public static Bitmap decodeFileDescriptor(FileDescriptor fd) //从文件读取文件 与decodeFile不同的是这个直接调用JNI函数进行读取 效率比较高
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

Option 参数类

public boolean inJustDecodeBounds //如果设置为true,不获取图片,不分配内存,但会返回图片的高度宽度信息。如果将这个值置为true,那么在解码的时候将不会返回bitmap,只会返回这个bitmap的尺寸。这个属性的目的是,如果你只想知道一个bitmap的尺寸,但又不想将其加载到内存时。这是一个非常有用的属性。
public int inSampleSize //图片缩放的倍数, 这个值是一个int,当它小于1的时候,将会被当做1处理,如果大于1,那么就会按照比例(1 / inSampleSize)缩小bitmap的宽和高、降低分辨率,大于1时这个值将会被处置为2的倍数。例如,width=100,height=100,inSampleSize=2,那么就会将bitmap处理为,width=50,height=50,宽高降为1 / 2,像素数降为1 / 4。
public int outWidth //获取图片的宽度值
public int outHeight //获取图片的高度值 ,表示这个Bitmap的宽和高,一般和inJustDecodeBounds一起使用来获得Bitmap的宽高,但是不加载到内存。
public int inDensity //用于位图的像素压缩比
public int inTargetDensity //用于目标位图的像素压缩比(要生成的位图)
public byte[] inTempStorage  //创建临时文件,将图片存储
public boolean inScaled //设置为true时进行图片压缩,从inDensity到inTargetDensity
public boolean inDither  //如果为true,解码器尝试抖动解码
public Bitmap.Config inPreferredConfig  //设置解码器,这个值是设置色彩模式,默认值是ARGB_8888,在这个模式下,一个像素点占用4bytes空间,一般对透明度不做要求的话,一般采用RGB_565模式,这个模式下一个像素点占用2bytes。
public String outMimeType  //设置解码图像
public boolean inPurgeable //当存储Pixel的内存空间在系统内存不足时是否可以被回收
public boolean inInputShareable  //inPurgeable为true情况下才生效,是否可以共享一个InputStream
public boolean inPreferQualityOverSpeed //为true则优先保证Bitmap质量其次是解码速度
public boolean inMutable  //配置Bitmap是否可以更改,比如:在Bitmap上隔几个像素加一条线段
public int inScreenDensity  //当前屏幕的像素密度

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/458924.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++篇----类、封装、类访问权限、类实例化

文章目录 一、面向过程和面向对象二、类 一、面向过程和面向对象 c语言是面向过程的编程语言 c是面向对象的编程语言 面向过程:关注过程,对于求解问题的不走,调用函数逐步解决问题 就洗衣服来说:洗衣服需要放水,倒洗衣…

PCL点云库(2) — IO模块

目录 2.1 IO模块接口 2.2 PCD数据读写 (1) PCD数据解析 (2)PCD文件读写示例 2.3 PLY数据读写 (1)PLY数据解析 (2)PLY文件读写示例 2.4 OBJ数据读写 (1&#xff…

QMS-云质说质量 - 10 我和我的客户投诉(2) - 客户逐利 驱除良币

云质QMS原创 转载请注明来源 作者:王洪石 上策伐谋 中策伐交 前面发过一篇关于客户投诉的文章“逢年过节要祈祷”,引起了很多质量人的共鸣,特别是汽车零部件行业曾经和正在负责客诉的质量同行们。 真实的产品质量问题,是否发生只…

Linux-初学者系列2——用户组管理和权限管理

用户组管理和权限管理 Linux-初学者系列2_用户组管理和权限管理一、所有者1、查看文件的所有者指令 2、修改文件所有者指令实操 二、组创建语法指令:实操: 三、所在组1、查看文件/目录所在组基本指令:实操: 2、修改文件所在组基本…

【读书笔记】高效能人士的7个习惯

高效能人士的7个习惯-史蒂芬柯维 个人成功1. 积极主动(BE PROACTIVE)2. 以终为始(BEGIN WITH THE END IN MIND)3. 要事第一(PUT FIRST THINGS FIRST)个人成功总结 集体成功4. 双赢思维(THINK WI…

flex布局属性详解

Flex布局 flex-directionflex-wrapflex-flowjustify-contentalign-itemsalign-content其他orderflexalign-self 含义:Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。 flex-direction flex-direction属性决定主轴的方向&…

服务(第十二篇)LVS-DR模式

数据包流向分析: (1)客户端发送请求到 Director Server(负载均衡器),请求的数据报文(源 IP 是 CIP,目标 IP 是 VIP)到达内核空间。 (2)Director Server 和 Re…

022 - C++ 析构函数

上期我们讨论了构造函数。认识了它是什么以及如何使用它。如果你没有看上一期,那么你一定要回去看一下。 今天我们要讨论一下它的“孪生兄弟”,析构函数,它们在某些方面非常相似。 构造函数是你创建一个新的实例对象时运行,而析…

无线测温系统在煤矿高压电气设备上的应用

摘要:随着社会经济的不断发展,电力系统向着高电压、高容量的方向前进着,电力系统全新的技术与设备层出不穷,电力的输送能力不断提升。然而,高压电气设备承载的高压电力负荷也让其自身的温升问题成为了威胁电网稳定的元…

张驰咨询:企业如何在不确定的环境中逆势增长?

企业不确定环境主要包括以下几个方面: 1、宏观经济环境的不确定性 包括国内外经济形势、政策调整、外汇汇率等因素的变化,会对企业的发展带来不确定性。 2、市场需求的不确定性 市场需求的变化,包括消费者需求、市场规模、市场结构等方面…

【自制键盘01】CH9329代码两则,让任何单片机都能做键盘

简介 CH9329是一款由WCH(Nanjing QinHeng Electronics Co. Ltd.)生产的USB转串口芯片,可以方便地将USB接口转换为串口接口,它在键盘设计这块可以实现作为MCU和电脑设备的“中间人”,把串口信号转换为按键。 引脚定义 …

如何实现电脑通过手机上网?1分钟搞定!

案例:电脑没网时,如何通过手机上网? 【想用电脑看电影,但是附近没有Wi-Fi。朋友说可以说电脑可以通过手机上网,但我们都不知道具体如何操作,有没有小伙伴可以教教我们。】 在没有Wi-Fi或有线网络接入时&a…

《Left ventricular hypertrophy detection using electrocardiographic signal》阅读笔记

论文的摘要 Left ventricular hypertrophy (LVH) indicates subclinical organ damage, associating with the incidence of cardiovascular diseases. From the medical perspective, electrocardiogram (ECG) is a low-cost, non-invasive, and easily reproducible tool th…

低代码平台-宜搭的核心概念

宜搭的核心概念 文章目录 全局变量基本的变量使用查看输出内容以及调试方式事件绑定页面生命周期条件渲染循环渲染自定义样式表单校验 全局变量 在左侧数据源中添加变量,添加变量的写法和js的写法一致。 基本的变量使用 给文本绑定数据源,点击左侧刚才定…

蒸发器前氟离子超标的解决方法

深度除氟工艺 1、活性氧化铝:需PH调整至酸性 2、碳基/羟基磷灰石:再生次数有限制 3、反渗透膜:造价成本高 4、特种除氟树脂:预处理需做好 氟化物选择吸附树脂 Tulsimer CH-87 是一款去除水溶液中氟离子的专用的凝胶型选择性…

LVS负载均衡之DR模式

DR调度服务器 192.168.255.128 Nginx节点服务器1 192.168.255.130 Nginx节点服务器2 192.168.255.131 统一虚拟ip(vip) 192.168.255.188 访问客户端 192.168.255.134 第一步首先关掉所有虚拟机的防火墙 systemctl stop firewalld.service setenfor…

Maya - 后缀为xgen文件导出到虚幻引擎

Xgen是集成在Maya中的工具,可以在指定模型表面生成和控制大量物体的集成和离散;经常用于复杂的毛发制作,可以方便的用笔刷等控制曲线(curves)和导引线(guides)等线条来控制毛发的走向&#xff1…

【MySQL】插入文件路径,反斜杠消失

系列文章 C#底层库–MySQL脚本自动构建类(insert、update语句生成) 本文链接:https://blog.csdn.net/youcheng_ge/article/details/129179216 C#底层库–MySQL数据库访问操作辅助类(推荐阅读) 本文链接:h…

克隆Linux系统(centos)

克隆前得保证你有一台Linux系统的虚拟机了。 如果没有,可以参考这篇文章: 安装VMware虚拟机、Linux系统(CentOS7)_何苏三月的博客-CSDN博客 按照示意图一步一步执行即可。 克隆前先关闭运行的虚拟机系统。 然后右键已安装的虚拟…

看完就懂的vue2与vue3响应式的区别

一、前言 数据响应式 所谓数据响应式就是建立响应式数据与依赖(调用了响应式数据的操作)之间的关系,当响应式数据发生变化时,可以通知那些使用了这些响应式数据的依赖操作进行相关更新操作,可以是DOM更新,也…