Bitmap--初探篇

前言

Bitmap代表一个位图,通过BitmapDrawable的构造方法可以把Bitmap对象包装成BitmapDrawable。
如果需要获取 BitmapDrawable 所包装的 Bitmap 对象,则可以用 BitmapDrawable 的 getBitmap() 的方法
参考博客: https://blog.csdn.net/wanliguodu/article/details/84973846

Bitmap创建

通过其静态方法createBitmap创建,一共有13种,分三类

1.根据已有的Bitmap来创建新Bitmap

通过矩阵的方式,返回原始 Bitmap 中的一个不可变子集。新 Bitmap 可能返回的就是原始的 Bitmap,也可能还是复制出来的。新 Bitmap 与原始 Bitmap 具有相同的密度(density)和颜色空间;

/**
* @param source   原始 Bitmap
* @param x        在原始 Bitmap 中 x方向的其起始坐标(你可能只需要原始 Bitmap x方向上的一部分)
* @param y        在原始 Bitmap 中 y方向的其起始坐标(你可能只需要原始 Bitmap y方向上的一部分)
* @param width    需要返回 Bitmap 的宽度(px)(如果超过原始Bitmap宽度会报错)
* @param height   需要返回 Bitmap 的高度(px)(如果超过原始Bitmap高度会报错)
* @param m        Matrix类型,表示需要做的变换操作
* @param filter   是否需要过滤,只有 matrix 变换不只有平移操作才有效
*/
public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,
            @Nullable Matrix m, boolean filter) 
2.通过像素点数组创建空的Bitmap

返回具有指定宽度和高度的不可变位图,每个像素值设置为colors数组中的对应值。其初始密度由给定的确定DisplayMetrics。新创建的位图位于sRGB 颜色空间中。

/**
     * @param display  显示将显示此位图的显示的度量标准
     * @param colors   用于初始化像素的sRGB数组
     * @param offset   颜色数组中第一个颜色之前要跳过的值的数量
     * @param stride   行之间数组中的颜色数(必须>= width或<= -width)
     * @param width    位图的宽度
     * @param height   位图的高度
     * @param config   要创建的位图配置。如果配置不支持每像素alpha(例如RGB_565),
     * 那么colors []中的alpha字节将被忽略(假设为FF)
     */
    public static Bitmap createBitmap(@NonNull DisplayMetrics display,
            @NonNull @ColorInt int[] colors, int offset, int stride,
            int width, int height, @NonNull Config config) 
3.创建缩放的Bitmap

对Bitmap进行缩放,缩放成宽 dstWidth、高 dstHeight 的新Bitmap

public static Bitmap createScaledBitmap(@NonNull Bitmap src, int dstWidth, int dstHeight,boolean filter)

BitmapFactory

表示从各种源文件中创建Bitmap对象,包括文件、流和字节数组等
BitmapFactory创建Bitmap的方法

  1. decodeByteArray(byte[] data, int offset, int length, Options opts):从指定字节数组的 offset 位置开始,将长度 length 的字节数据解析成 Bitmap 对象。
  2. decodeFile(String pathName, Options opts):从 pathName 指定的文件中解析、创建 Bitmap 对象。
  3. decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts):用于从 FileDescriptor 对应的文件中解析、创建 Bitmap 对象。
  4. decodeResource(Resources res, int id, Options opts):用于给定的资源 ID 从指定资源中解析、创建 Bitmap 对象。
  5. .decodeStream(InputStream is, Rect outPadding, Options opts):用于从指定输入流中解析、创建 Bitmap 对象。

注意:decodeResource 在解析时会对 Bitmap 根据当前设备屏幕密度 densityDpi 的值进行缩放适配操作,使得解析出来的 Bitmap 与当前设备分辨率匹配,并且一般来说,这时 Bitmap 的大小将比原始的 Bitmap 大。
decodeFile、decodeStream 在解析时不会对 Bitmap 进行一系列的屏幕适配,解析出来的将是原始大小的图。

BitmapFactory.Options的属性

  1. 1.inJustDecodeBounds:只想知道bitmap的尺寸但又不想将其加载进内存则使用这个属性,为true,在解码的时候不返回bitmap对象,只返回bitmap尺寸
  2. outWidth和outHeight:表示这个 Bitmap 的宽和高,一般和 inJustDecodeBounds 一起使用来获得 Bitmap的宽高,但是不加载到内存。
  3. inSampleSize:压缩图片时采样率的值,如果这个值大于1,那么就会按照比例(1 / inSampleSize)来缩小 Bitmap 的宽和高。如果这个值为 2,那么 Bitmap 的宽为原来的1/2,高为原来的1/2,那么这个 Bitmap是所占内存像素值会缩小为原来的 1/4
  4. inDensity:表示这个 Bitmap 的像素密度
  5. inTargetDensity:表示要被新 Bitmap 的目标像素密度,对应的是 DisplayMetrics 中的 densityDpi
  6. inScreenDensity:表示实际设备的像素密度,对应的是 DisplayMetrics 中的 densityDpi
  7. inPreferredConfig:这个值是设置色彩模式,默认值是 ARGB_8888,这个模式下,一个像素点占用 4Byte 。RGB_565 占用 2Byte,ARGB_4444 占用 4Byte(已废弃)
  8. inPremultiplied:这个值和透明度通道有关,默认值是 true,如果设置为 true,则返回的 Bitmap 的颜色通道上会预先附加上透明度通道。
  9. inDither:这个值和抖动解码有关,默认值为 false,表示不采用抖动解码
  10. inScaled:设置这个Bitmap 是否可以被缩放,默认值是 true,表示可以被缩放。
  11. inPreferQualityOverSpeed:这个值表示是否在解码时图片有更高的品质,仅用于 JPEG 格式。如果设置为 true,则图片会有更高的品质,但是会解码速度会很慢。
  12. inBitmap:这个参数用来实现 Bitmap 内存的复用,但复用存在一些限制,具体体现在:在 Android 4.4 之前只能重用相同大小的 Bitmap 的内存,而 Android 4.4 及以后版本则只要后来的 Bitmap 比之前的小即可。使用inBitmap 参数前,每创建一个 Bitmap 对象都会分配一块内存供其使用,而使用了 inBitmap 参数后,多个 Bitmap可以复用一块内存,这样可以提高性能。

计算Bitmap的大小

一行图片所占的内存计算公式:
图片的占用内存 = 图片的长度(像素单位) * 图片的宽度(像素单位) * 单位像素所占字节数

Bitmap的缩放

质量压缩
不改变图片的像素点,即只减少图片存储在本地的大小,不改变Bitamap占用内存的大小

/**
     * 质量压缩方法,并不能减小加载到内存时所占用内存的空间,应该是减小的所占用磁盘的空间
     * @param image
     * @param compressFormat
     * @return
     */
    public static Bitmap compressbyQuality(Bitmap image, Bitmap.CompressFormat compressFormat) {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(compressFormat, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        int quality = 100;

        while ( baos.toByteArray().length / 1024 > 100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
            baos.reset();//重置baos即清空baos
            if(quality > 10){
                quality -= 20;//每次都减少20
            }else {
                break;
            }
            image.compress(Bitmap.CompressFormat.JPEG, quality, baos);//这里压缩options%,把压缩后的数据存放到baos中

        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        Bitmap bmp = BitmapFactory.decodeStream(isBm, null, options);//把ByteArrayInputStream数据生成图片

        return bmp;
    }

采样压缩
主要用在图片资源本身较大,或者适当地采样并不会影响视觉效果的条件下,这时候我们输出的目标可能相对的较小,对图片的大小和分辨率都减小。减少Bitmap占用内存大小,但是图的尺寸也小

BitmapFactory.Options options = new Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId, options);

使用矩阵

/**
     * 矩阵缩放图片
     * @param sourceBitmap
     * @param width 要缩放到的宽度
     * @param height 要缩放到的长度
     * @return
     */
    private Bitmap getScaleBitmap(Bitmap sourceBitmap,float width,float height){
        Bitmap scaleBitmap;
        //定义矩阵对象
        Matrix matrix = new Matrix();
        float scale_x = width/sourceBitmap.getWidth();
        float scale_y = height/sourceBitmap.getHeight();
        matrix.postScale(scale_x,scale_y);

        try {
            scaleBitmap = Bitmap.createBitmap(sourceBitmap,0,0,sourceBitmap.getWidth(),sourceBitmap.getHeight(),matrix,true);
        }catch (OutOfMemoryError e){
            scaleBitmap = null;
            System.gc();
        }
        return scaleBitmap;
    }

综合优化
获取采样的流程:
1.将 BitmapFactory.Options 的 inJustDecodeBounds 参数设置为 true 并加装图片。
2.从 BitmapFactory.Options 中取出图片的原始宽和高,它们对应于 outWidth 和 outHeight 参数。
3.根据采样率的规则并结合目标 View 的所需要大小计算出采样率 inSampleSize 。
4.将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 false ,然后重新加装图片。

/**
     * 采样率压缩,这个和矩阵来实现缩放有点类似,但是有一个原则是“大图小用用采样,小图大用用矩阵”。
     * 也可以先用采样来压缩图片,这样内存小了,可是图的尺寸也小。如果要是用 Canvas 来绘制这张图时,再用矩阵放大
     * @param image
     * @param compressFormat
     * @param requestWidth 要求的宽度
     * @param requestHeight 要求的长度
     * @return
     */
    public static Bitmap compressbySample(Bitmap image, Bitmap.CompressFormat compressFormat, int requestWidth, int requestHeight){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(compressFormat, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inPurgeable = true;
        options.inJustDecodeBounds = true;//只读取图片的头信息,不去解析真实的位图
        BitmapFactory.decodeStream(isBm,null,options);
        options.inSampleSize = calculateInSampleSize(options,requestWidth,requestHeight);
        //-------------inBitmap------------------
        options.inMutable = true;
        try{
            Bitmap inBitmap = Bitmap.createBitmap(options.outWidth, options.outHeight, Bitmap.Config.RGB_565);
            if (inBitmap != null && canUseForInBitmap(inBitmap, options)) {
                options.inBitmap = inBitmap;
            }
        }catch (OutOfMemoryError e){
            options.inBitmap = null;
            System.gc();
        }

        //---------------------------------------

        options.inJustDecodeBounds = false;//真正的解析位图
        isBm.reset();
        Bitmap compressBitmap;
        try{
            compressBitmap =  BitmapFactory.decodeStream(isBm, null, options);//把ByteArrayInputStream数据生成图片
        }catch (OutOfMemoryError e){
            compressBitmap = null;
            System.gc();
        }

        return compressBitmap;
    }

    /**
     * 采样压缩比例
     * @param options
     * @param reqWidth 要求的宽度
     * @param reqHeight 要求的长度
     * @return
     */
    private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

        int originalWidth = options.outWidth;
        int originalHeight = options.outHeight;
        
        int inSampleSize = 1;

        if (originalHeight > reqHeight || originalWidth > reqHeight){
            // 计算出实际宽高和目标宽高的比率
            final int heightRatio = Math.round((float) originalHeight / (float) reqHeight);
            final int widthRatio = Math.round((float) originalWidth / (float) reqWidth);
            // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
            // 一定都会大于等于目标的宽和高。
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

        }
        return inSampleSize;
    }
Bitmap的像素格式

在这里插入图片描述

获取Bitmap的几种方式总结

(1) public Bitmap (int width,int height,int stride, PixelFormat format,IntPtr scan0)
用指定的大小、像素格式和像素数据初始化 Bitmap 类的新实例。
(2) LockBits():,就是把图像的内存区域根据格式锁定,拿到那块内存的首地址。这样就可以直接改写这段内存了。这个方法的设计是挺好,可惜都是C++作为源泉来的,.NET Framework里面根本就不推荐用指针,需要用Marshal.Copy把内容Copy到一个byte数组里面,然后处理完了再Copy回去。
(3) UnlockBits():从系统内存解锁此 Bitmap。

从获取方式分

(1)以文件流的方式

//假设在sdcard下有 test.png图片
FileInputStream fis = new FileInputStream("/sdcard/test.png"); 
Bitmap bitmap=BitmapFactory.decodeStream(fis);

(2)以R文件的方式

//假设 res/drawable下有 test.jpg文件
Bitmap bitmap =BitmapFactory.decodeResource(getResources(), R.drawable.test);
//或者
BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.test);
Bitmap bitmap = bitmapDrawable.getBitmap();

(3)以ResourceStream的方式,不用R文件

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

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

InputStream in = getResources().openRawResource(R.drawable.test);
Bitmap bitmap = BitmapFactory.decodeStream(in);
//或
InputStream in = getResources().openRawResource(R.drawable.test);
BitmapDrawable bitmapDrawable = new BitmapDrawable(in);
Bitmap bitmap = bitmapDrawable.getBitmap();

注意:openRawResource可以打开drawable, sound, 和raw资源,但不能是string和color。

从资源存放路径分

(1)图片放在sdcard中

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

(2)图片在项目的res文件夹下面

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

(3)图片放在src目录下

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

(4)图片放在Assets目录

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

总结

获取Bitmap的方法有很多,但是在实际开发中一定要注意图片的内存大小,即使获得了Bitmap也需要对它进行压缩等操作,防止出现内存过大导致系统crash。

注意:是的没错我是把上面这个https://blog.csdn.net/wanliguodu/article/details/84973846博客拿来粘贴复制了一些,虽然加了一点自己的话,但是基本都是复制的,菜鸟一枚,不要见怪。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常抱歉,我之前提供的代码中确实存在错误。在调用 `bitmap->GetHBITMAP()` 函数时,需要传递一个 `ColorPalette` 对象作为参数。以下是修正后的代码: ```cpp // ... void OpenFile() { OPENFILENAME ofn; wchar_t szFile[MAX_PATH] = { 0 }; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = g_hWnd; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = L"PNG文件\0*.png\0"; ofn.nFilterIndex = 1; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; if (GetOpenFileName(&ofn)) { Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(szFile); if (bitmap->GetLastStatus() == Gdiplus::Ok) { if (g_hBitmap != NULL) { DeleteObject(g_hBitmap); } // 获取图像的宽度和高度 int width = bitmap->GetWidth(); int height = bitmap->GetHeight(); // 创建与图像大小相匹配的内存设备上下文 HDC hdcScreen = GetDC(NULL); HDC hdcMem = CreateCompatibleDC(hdcScreen); g_hBitmap = CreateCompatibleBitmap(hdcScreen, width, height); SelectObject(hdcMem, g_hBitmap); // 使用GDI+将图像绘制到内存设备上下文 Gdiplus::Graphics graphics(hdcMem); graphics.DrawImage(bitmap, 0, 0, width, height); // 释放资源 DeleteDC(hdcMem); ReleaseDC(NULL, hdcScreen); SendMessage(g_hImage, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hBitmap); EnableWindow(g_hButtonSave, TRUE); } delete bitmap; } } // ... ``` 请注意,我们添加了一些代码来处理图像的宽度和高度,并创建与图像大小相匹配的内存设备上下文。这样可以确保在加载图像时保持其原始大小。 再次感谢您的指出,如果还有其他问题,请随时提问!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值