一.Bitmap

内容如下:

1.Bitmap的生成

2.bitmap缩放、等图像变换

3.bitmap模糊处理

4.bitmap保存图像文件

5.Bitmap的防止内存泄露小方法

6.小知识点

 

1.Bitmap的生成

/**
     * 由本地文件路径、网络url或者项目的资源文件,生成Bitmap(旧,极端情况下可能造成OOM)
     * @param filePath
     */
    private void productBitmap(String filePath){
        Bitmap des_bitmap = null;
        BitmapFactory.Options options  = new BitmapFactory.Options();
//        options.inPreferredConfig
        //本地文件路径或者网络url
        Uri uri = Uri.parse(filePath);
        des_bitmap = BitmapFactory.decodeFile(uri.toString(),options);

        //项目资源文件
        des_bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);

        //流,例如文件流
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(filePath);
            des_bitmap = BitmapFactory.decodeStream(fis,null,options);
            fis.close();
            fis = null;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if (fis != null)
                    fis.close();
            } catch (Exception e) {

            }
        }

        if(iv_bitmap_test!=null) {
            iv_bitmap_test.setImageBitmap(des_bitmap);
        }else{
            iv_bitmap_test = (ImageView) findViewById(R.id.iv_bitmap_test);
            iv_bitmap_test.setImageBitmap(des_bitmap);
        }
    }

 

/**
     * 获取截屏
     *
     * @param view 要截屏的view
     * @return 位图
     */
    private Bitmap getScreenShot(View view) {
        View v = view;
        v.setDrawingCacheEnabled(true);
        Bitmap bitmap = Bitmap.createBitmap(v.getDrawingCache());
        return bitmap;
    }

  

  

 

2.bitmap缩放、等图像变换

(1)长宽相同比例缩放

用BitmapFactory的decodeFile方法,然后通过传递进去 BitmapFactory.Option类型的参数进行取缩略图,在Option中,属性值inSampleSize表示缩略图大小为原始图片大小的几分之一,即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。

经过阅读文档发现,Options中有个属性inJustDecodeBounds,文档中的是这么说的:

If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.  

意思就是说如果该值设为true那么将不返回实际的bitmap对象,不给其分配内存空间但是可以得到一些解码边界信息即图片大小等信息。因此我们可以通过设置inJustDecodeBounds为true,获取到outHeight(图片原始高度)和 outWidth(图片的原始宽度),然后计算一个inSampleSize(缩放值),就可以取图片了,这里要注意的是,inSampleSize 可能等于0,必须做判断。也就是说先将Options的属性inJustDecodeBounds设为true,先获取图片的基本大小信息数据(信息没有保存在bitmap里面,而是保存在options里面),通过options.outHeight和 options. outWidth获取的大小信息以及自己想要到得图片大小计算出来缩放比例inSampleSize,然后紧接着将inJustDecodeBounds设为false,就可以根据已经得到的缩放比例得到自己想要的图片缩放图了。

/**
     * 由File转成长宽固定相同比例的bitmap。实现长宽等比例缩放
     * @return
     */
    private Bitmap resizeBitmap(String filePath){
        Bitmap des_bitmap ;

        BitmapFactory.Options options = new BitmapFactory.Options();
        //表示缩略图大小为原始图片大小的几分之一,即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4
        options.inJustDecodeBounds = true;
        des_bitmap = BitmapFactory.decodeFile(filePath,options);//des_bitmap为null.因为options.inJustDecodeBounds = true,节省内存

        //长宽按照相同比例缩放
        int height = options.outHeight;
        int width = options.outWidth;
        int scale;//缩放值,因为为等比例缩放.
        if(height > width){//以高为准
            scale = (int) (height/200.0f);//该200说明目标大小为200PX
        }else{
            scale = (int) (width/200.0f);//该200说明目标大小为200PX
        }
        if(scale<=0){
            scale = 1;
        }
        options.inSampleSize = scale;
        options.inJustDecodeBounds = false;
        Bitmap new_bitmap = BitmapFactory.decodeFile(filePath,options);//des_bitmap不为null
        if(des_bitmap!=new_bitmap){
            des_bitmap.recycle();//注意销毁不用的bitmap
        }
        return new_bitmap;
    }

 

(2)Bitmap与Matrix搭配,实现缩放或者其他形变例如旋转

如果对Matrix矩阵类没有多少概念或者概念不全,可进入Matrix详情了解

/**
     * 由Bitmap转成指定大小的Bitmap,实现缩放成指定准确大小Bitmap
     * @param filePath
     * @param sWidth 目标宽,单位为px
     * @param sHeight 目标高,单位为px
     */
    private Bitmap resizeBitmap(String filePath,int sWidth,int sHeight){
        //缩放到指定的长宽
        Bitmap mBitmap;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
        mBitmap = BitmapFactory.decodeFile(filePath,options);
        if(mBitmap == null){
            return null;
        }
        int bmpWidth = mBitmap.getWidth();
        int bmpHeight = mBitmap.getHeight();
        // 缩放图片的尺寸
        float scaleWidth = (float) sWidth / bmpWidth; // 按固定大小缩放 sWidth 写多大就多大
        float scaleHeight = (float) sHeight / bmpHeight; //
        Matrix matrix = new Matrix();
    //matrix.postScale(1, -1); //镜像垂直翻转
    //matrix.postScale(-1, 1); //镜像水平翻转
    //matrix.postRotate(-90); //旋转-90度
matrix.postScale(scaleWidth, scaleHeight);// 产生缩放后的Bitmap对象 
Bitmap resizeBitmap = Bitmap.createBitmap(mBitmap, 0, 0, bmpWidth, bmpHeight, matrix, false);
mBitmap.recycle();
return resizeBitmap; }

 在控件画布上的使用:

重要的方法有

Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter){}
canvas.drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {}

 

Matrix mIconMatrix= new Matrix();
mIconMatrix.postScale(0.71f,0.71f);
final Bitmap start = BitmapFactory.decodeResource(getResources(), R.drawable.run_start);
bitmapStart = Bitmap.createBitmap(start,0,0,start.getWidth(),start.getHeight(),mIconMatrix,false);
protected void onDraw(Canvas canvas){
   mIconMatrix.postTranslate(123,123);//matrix的一系列变换
       canvas.drawBitmap(bitmapStart, mIconMatrix, null);
}

 

 

3.bitmap模糊处理

(一)自定义的模糊

 见下篇文章

(二)高斯模糊

下篇文章介绍

4.bitmap保存图像文件

(一)保存

/**
     * 保存内存中的bitmap到本地文件
     * @param bitmap 要保存的bitmap
     * @param localPath 保存在本地的文件名,绝对路径
     * */
    public static void saveBitmap2File(Bitmap bitmap,String localPath){
        if (bitmap == null) {
            return;
        }
        try {
            File file = new File(localPath);
            FileOutputStream fos = new FileOutputStream(file);
            assert bitmap != null;
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);//100表示正常质量,如果压缩之类的更改压缩质量即可
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 (二)压缩

/**
     * 圆片压缩
     * 
     * @param bitmap
     * @return
     */
    private Bitmap compressImage(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
      Log.e("size", baos.toByteArray().length/1024+"");
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
        return bitmap;
    }

(三)Bitmap<->bytes转换

1.  public static Bitmap Bytes2Bimap(byte[] b) {  
2.         if (b.length != 0) {  
3.             return BitmapFactory.decodeByteArray(b, 0, b.length);  
4.         } else {  
5.             return null;  
6.         }  
7.     }  
1.  public static byte[] Bitmap2Bytes(Bitmap bm) {  
2.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
3.         bm.compress(Bitmap.CompressFormat.PNG, 100, baos);  
4.         return baos.toByteArray();  
5.     }  
6. 
 
1.  public static Bitmap drawableToBitmap(Drawable drawable) {  
2.   
3.         Bitmap bitmap = Bitmap  
4.                 .createBitmap(  
5.                         drawable.getIntrinsicWidth(),  
6.                         drawable.getIntrinsicHeight(),  
7.                         drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888  
8.                                 : Bitmap.Config.RGB_565);  
9.         Canvas canvas = new Canvas(bitmap);  
10.         // canvas.setBitmap(bitmap);  
11.         drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),  
12.                 drawable.getIntrinsicHeight());  
13.         drawable.draw(canvas);  
14.         return bitmap;  
15.     } 

 

5.Bitmap的防止内存泄露小方法

(一)Bitmap占用了多少内存

①获取bitmap的内存大小

相关:

/**
     * Returns the minimum number of bytes that can be used to store this bitmap's pixels.
     *
     * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, the result of this method can
     * no longer be used to determine memory usage of a bitmap. See {@link
     * #getAllocationByteCount()}.</p>
     */
    public final int getByteCount() {
        // int result permits bitmaps up to 46,340 x 46,340
        return getRowBytes() * getHeight();
    }

getByteCount()方法能获取该bitmap所占的最小内存,不过API19即4.4以后通过getAllocationByteCount()获取内存大小。

两者可能存在不一样的原因是因为如果bitmap重复利用的话,先存储形状大的图片再存储形状小些的图片,对于小的图片,通过getAllocationCount()获取到的大小会比getByteCount()要大。

/**
     * Returns the size of the allocated memory used to store this bitmap's pixels.
     *
     * <p>This can be larger than the result of {@link #getByteCount()} if a bitmap is reused to
     * decode other bitmaps of smaller size, or by manual reconfiguration. See {@link
     * #reconfigure(int, int, Config)}, {@link #setWidth(int)}, {@link #setHeight(int)}, {@link
     * #setConfig(Bitmap.Config)}, and {@link BitmapFactory.Options#inBitmap
     * BitmapFactory.Options.inBitmap}. If a bitmap is not modified in this way, this value will be
     * the same as that returned by {@link #getByteCount()}.</p>
     *
     * <p>This value will not change over the lifetime of a Bitmap.</p>
     *
     * @see #reconfigure(int, int, Config)
     */
    public final int getAllocationByteCount() {
        if (mBuffer == null) {
            // native backed bitmaps don't support reconfiguration,
            // so alloc size is always content size
            return getByteCount();
        }
        return mBuffer.length;
    }
②深入

在getByteCount()中,getHeight()标示bitmap的高度,单位为px。而getRowBytes()呢?往下

/**
     * Return the number of bytes between rows in the bitmap's pixels. Note that
     * this refers to the pixels as stored natively by the bitmap. If you call
     * getPixels() or setPixels(), then the pixels are uniformly treated as
     * 32bit values, packed according to the Color class.
     *
     * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, this method
     * should not be used to calculate the memory usage of the bitmap. Instead,
     * see {@link #getAllocationByteCount()}.
     *
     * @return number of bytes between rows of the native bitmap pixels.
     */
    public final int getRowBytes() {
        if (mRecycled) {
            Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!");
        }
        return nativeRowBytes(mNativePtr);
    }

进入Android5.0源码进行JNI分析

\frameworks\base\core\jni\Android\graphics\Bitmap.cpp:

static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle)
     return static_cast<jint>(bitmap->rowBytes());
}

我们发现rowBytes()其实就是对SKBitmap的指针引用。因为bitmap指向的是skBitmap的所在地址。所以bitmap的大小即为SkBitmap的值。

SkBitmap.h:

/** Return the number of bytes between subsequent rows of the bitmap. */
size_t rowBytes() const { return fRowBytes; }

SkBitmap.cpp:

size_t SkBitmap::ComputeRowBytes(Config c, int width) {
    return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width);
}

static inline size_t SkColorTypeMinRowBytes(SkColorType ct, int width) {
    return width * SkColorTypeBytesPerPixel(ct);
}

SkImageInfo.h
 
static int SkColorTypeBytesPerPixel(SkColorType ct) {
   static const uint8_t gSize[] = {
    0,  // Unknown
    1,  // Alpha_8
    2,  // RGB_565
    2,  // ARGB_4444
    4,  // RGBA_8888
    4,  // BGRA_8888
    1,  // kIndex_8
  };
  SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1),
                size_mismatch_with_SkColorType_enum);
 
   SkASSERT((size_t)ct < SK_ARRAY_COUNT(gSize));
   return gSize[ct];
}
 

我发现大小即是width和一个ColorTypeBytesPerPixel的乘积。ColorTypeBytesPerPixel为bitmap的图片分辨率类型,在BitmapFactory.Option的inPreferredConfig属性决定,即不同图片

分辨率类型决定了不同的内存大小(例如ARGB_8888类型的分辨率图片每单位width下占4字节,该类型也是Bitmap默认的属性)。即rowBytes等于‘4*width’ Bytes。

但是通过计算,发现通过计算内存=4*width*height,仍然不对。

 

因此我猜想图片的内存大小除了图片自身的分辨率以外,还跟设备的显示密度、以及资源来源有关。

 

③再深入

我们以BitmapFactory.decodeResource()加载项目资源文件为例

/**
     * Synonym for opening the given resource and calling
     * {@link #decodeResourceStream}.
     *
     * @param res   The resources object containing the image data
     * @param id The resource id of the image data
     * @param opts null-ok; Options that control downsampling and whether the
     *             image should be completely decoded, or just is size returned.
     * @return The decoded bitmap, or null if the image data could not be
     *         decoded, or, if opts is non-null, if opts requested only the
     *         size be returned (in opts.outWidth and opts.outHeight)
     */
    public static Bitmap decodeResource(Resources res, int id, Options opts) {
        Bitmap bm = null;
        InputStream is = null; 
        
        try {
            final TypedValue value = new TypedValue();
            is = res.openRawResource(id, value);

            bm = decodeResourceStream(res, value, is, null, opts);
        } catch (Exception e) {
            /*  do nothing.
                If the exception happened on open, bm will be null.
                If it happened on close, bm is still valid.
            */
        } finally {
            try {
                if (is != null) is.close();
            } catch (IOException e) {
                // Ignore
            }
        }

        if (bm == null && opts != null && opts.inBitmap != null) {
            throw new IllegalArgumentException("Problem decoding into existing bitmap");
        }

        return bm;
    }
View Code

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-08-30
  • 2021-12-02
  • 2022-02-01
  • 2022-12-23
  • 2022-12-23
  • 2021-04-10
猜你喜欢
  • 2021-09-27
  • 2022-12-23
  • 2021-12-12
  • 2022-12-23
  • 2021-12-19
  • 2021-06-02
相关资源
相似解决方案