认识 Android Bitmap

Bitmap 介绍

位图(Bitmap),又称栅格图(英语:Raster graphics)或点阵图,是使用像素阵列(Pixel-array/Dot-matrix点阵)来表示的图像。

位图的像素都分配有特定的位置和颜色值。每个像素的颜色信息由RGB组合或者灰度值表示。

根据位深度,可将位图分为1、4、8、16、24及32位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大。

以上内容均出自百度百科Bitmap词条

Android 中的Bitmap实现了 Parcelable 序列化接口,Bitmap的创建由 Native 曾实现,Bitmap 的内存分配在不同Android版本均有不同实现。此外 Bitmap 在内存中和磁盘中有两种不同表现形式:前者表示图片被加载到内存中,称之为 bitmap;后者则意味着图片被以不同压缩格式的方式存储到硬盘上。

图片压缩格式

JPEG

JPEG(Joint Photographic Experts Group) 格式是一种有损压缩格式,能够在不影响视觉效果的前提下大幅度减小文件体积,但代价是会损失部分图像信息,多次编辑和保存可能导致画质逐渐下降;JPEG格式支持用户自定义压缩质量级别,允许在文件大小和图像质量之间进行权衡。压缩程度越高,文件越小,但画质损失也越大。

JPEG图片以24位颜色压缩存储单个位图,采用YCbCr色彩空间进行编码,每个像素点只包含红(R)蓝(B)绿(G)三个颜色分量, 不支持透明通道,JPEG也不支持多帧动画,文件后缀为JPEG或JPG。

JPEG2000作为JPEG的升级版,其压缩率比JPEG高约30%左右,同时支持有损和无损压缩。JPEG2000格式有一个极其重要的特征在于它能实现渐进传输,即先传输图像的轮廓,然后逐步传输数据,不断提高图像质量,让图像由朦胧到清晰显示。

JPEG2000还支持所谓的“感兴趣区域”特性,也就是可以任意指定影像上感兴趣区域的压缩质量;另外,JPEG2000还可以选择指定的部分先解压缩来加载到内存中。JPEG2000和JPEG相比优势明显,且向下兼容,因此可取代传统的JPEG格式。

PNG

PNG(Portable Network Graphics)格式后缀缩写为.png,是无损压缩算法的位图格式,支持多种位深度(最高32位),可以为原图像定义256个透明层次,也就是说它支持所有的颜色类型。

  • 灰度图像:PNG 支持 1 位 ~ 16位的灰度图像

  • 彩色图像:

1. 索引色(Indexed Color)图像,PNG支持8位或更少的位深度,即最多256种颜色(每个像素用一个字节来表示索引到调色板的颜色);

2. 真彩色(Truecolor)图像,PNG标准支持24位(8位红、8位绿、8位蓝),可以显示大约1670万种不同的颜色

3. 包括Alpha通道(透明度信息),则为32位(24位RGB + 8位Alpha),这样每个像素除了有RGB颜色外,还有一个额外的8位用于表示透明度,允许像素具有从完全透明到完全不透明的平滑过渡。

同样是无损压缩,PNG的压缩率高于Gif格式,而且PNG支持的颜色数量也远高于Gif,因此:如果是对静态图片进行无损压缩,优先使用PNG取代Gif,因为PNG压缩率高、色彩好;但是PNG不支持动画效果。

此外,PNG文件中还可以存储元数据,如版权、描述等信息。

WebP

同时提供了有损压缩与无损压缩的图片文件格式。WebP支持的像素最大数量是16383x16383。有损压缩的WebP仅支持8-bit的YUV 4:2:0格式。而无损压缩(可逆压缩)的WebP支持VP8L编码与8-bit之ARGB色彩空间。无论无损还是有损皆支持Alpha透明通道、ICC色彩配置、XMP诠释数据。

WebP有静态与动态两种模式。动态WebP(Animated WebP)支持有损与无损压缩、ICC色彩配置、XMP诠释数据、Alpha透明通道。

WebP用 VP8 视频帧内编码作为其算法基础,WebP支持有损和无损压缩、支持完整的透明通道、也支持多帧动画,并且没有版权问题,是一种非常理想的图片格式。WebP支持动图,基本取代gif。

WebP不仅集成了PNG、JPEG和Gif的所有功能,而且相同质量的无损压缩WebP图片体积比PNG小大约26%;如果是有损压缩,相同质量的WebP图片体积比JPEG小25%-34%。

缺点

有损压缩的算法决定了其压缩时间一定是高于无损压缩的,也就是说JPEG的压缩时间高于PNG。而WebP无论是无损还是有损压缩,压缩率都分别高于PNG和JPEG,与其相对应的是其压缩时间也比它们长的多。

GIF

GIF(Graphics Interchange Format)是一种基于LZW算法的无损压缩格式,多次编辑保存可能产生轻微的质量损失。

Gif可插入多帧,从而实现动画效果。因此Gif图片分为静态GIF和动画GIF两种GIF格式。

由于Gif以8位颜色压缩存储单个位图,所以它最多只能用256种颜色来表现物体,对于色彩复杂的物体它就力不从心了。因此Gif不适合用于色彩非常丰富的图片的压缩存储,比如拍摄的真彩图片等。

GIF 支持单一颜色透明,即可以选择一种颜色作为透明色,在显示时背景会透过该颜色显示出来。这种透明效果是二元的,不支持半透明效果。

关于图片几个概念介绍

透明度通道

在图形图像学中,透明通道也称Alpha通道,表示在图像文件格式中用于表示每个像素点透明度信息的一个额外的色彩通道。

通常,一个颜色由红、绿、蓝三种基本色组成,在支持透明度的图像格式中,会增加一个称为Alpha的第四个通道,它使用8位二进制数(0到255之间的值)来表示从完全透明(0)到完全不透明(255)的不同程度。

在Android中的Bitmap中,当配置为ARGB_8888时,就表示每个像素包含4个8位的颜色通道,分别是Alpha、Red、Green和Blue,其中Alpha通道即负责控制像素的透明度。

  • 白色的Alpha像素用以定义不透明的彩色像素;
  • 黑色的Alpha定以透明像素;
  • 黑白之间的灰阶则是彩色图片中的半透明部分。

色深

色深指的是每一个像素点用多少bit来存储ARGB值,属于图片自身的一种属性。色深可以用来衡量一张图片的色彩处理能力(即色彩丰富程度)。典型的色深是8-bit、16-bit、24-bit和32-bit等,Bitmap.Config中描述的就是图片的色深。

例如,在24位真彩图像中,每个像素由红绿蓝三种颜色组成,每种颜色8位(位深度为8),每个 8位二进制数可以表示 0 到 255共256种不同的灰阶级别,有由于有三种颜色通道构成,共计可以表示约 1670万种颜色。

Bitmap.Config

Config:是一个枚举类,描述多种位图像素的存储方式

  • ALPHA_8:每个像素占用1byte,不会存储颜色信息,每个像素点存储为单个半透明通道,只存储透明度A通道值,使用场景特殊,比如设置遮盖效果等;
  • RGB_565:每个像素占用2byte,仅RGB通道被解码,红色和蓝色以5bit的精度存储(32种可能的值),绿色则是6bit(64种可能的值)
  • ARGB_8888:每像素占用4byte,每个通道以8bit精度存储,有256种可能的值
  • RGBA_F16:每个通道以 android.util.Half 的精度(16bit)的浮点数存储,适用于宽色域和HDR内容
  • HARDWARE:特殊配置,当bitmap仅存储在图形内存(graphic memory),这类型位图是不可变的

位深

位深指的是在对Bitmap进行压缩存储时存储每个像素所用的bit数,位数越多,图像的色彩也就越丰富细腻。由于是“压缩”存储,所以位深一般小于或等于色深 。

举个例子:某张图片100像素*100像素 色深32位(ARGB_8888),保存时位深度为24位,那么:

  • 该图片在内存中所占大小为:100 * 100 * (32 / 8) Byte
  • 在文件中所占大小为 100 * 100 * ( 24/ 8 ) * 压缩率 Byte

Android Bitmap 的API和 density 属性

Bitmap 的函数除了 Java 层级的函数外,还有 Native 层的函数以真正实现操作位图的功能。

Bitmap Java 层 API

创建和销毁
create系列

多个createBitmap:调用nativeCreate创建新的Bitmap对象

recycle

调用后不允许位图被继续操作,清理Native层的位图对象,并且清理像素引用。并非同步释放像素数据,而是标记bitmap为dead,表示允许释放。

获取位图信息
getRowBytes

返回每行所有像素占用的字节数量。

getByteCount

返回存储此位图像素的最小字节数。

getAllocationByteCount

返回实际分配的位图占用内存的大小,单位是 byte。

getPixel

返回Bitmap特定位置的颜色。

getColor

返回Bitmap特定位置的颜色。

isMutable

返回true表示位图可以修改。

编辑位图的API
setDensity

指定位图的密度。当绘制Bitmap到canvas时,使用density,绘制的位图会被适当的缩放。

  • bitmap.density 默认是设备屏幕的 density 大小
  • bitmap对象的density可以直接修改,bitmap 显示大小 随之改变,但不影响bitmap的内存占用和实际尺寸信息,仅仅是缩放了bitmap
setImmutable

使位图不可变,即不能再修改位图的任何配置、颜色等,例如 调用该方法后,reconfigure、setPixel、setPixels、eraseColor 等函数不允许被调用,否则就会抛出异常。

setHasMipMap

mipmap是一种纹理映射技术,Android使用它来提高bitmap的渲染速度和质量。setHasMipMap(true)时,可以让系统尝试开启mipmap技术,但是否真正开启由系统决定。

extractAlpha(Paint,int offsetx)

返回新的bitmap仅包含了原图的alpha通道,可用于显示阴影效果。

reconfigure

当修改位图的width/height或者Config时,触发这个方法。

Copy 系列函数
  • copy(Bitmap.Config,isMutable) :以指定色深和可变性拷贝出新的位图,返回空或新的Bitmap。部分Config不能完全支持;
  • copyPixelsToBuffer:拷贝位图像素信息到指定的缓存空间,缓存大小必须大于像素信息;有与之对应的方法 copyPixelsFromBuffer,从缓存区拷贝bitmap的像素信息
Compress 压缩图片

以指定的压缩图片格式和压缩率(0 ~ 100)压缩存储位图。

asShared

返回由共享内存支持的不可变位图,该位图可以通过包裹在进程之间高效传递。

sameAs(Bitmap other)

判断二者尺寸信息、Config、pixel信息是否一致,三个条件满足时返回true。

Android Bitmap density

density(密度),DisplayMatrix中可以获得当前设备的density,它和屏幕每平方英寸中所含像素数量有关,具体的计算公式:

density = dpi / 160

Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.a)
imageView.setBitmap(bitmap);

Bitmap bitmap2 = bitmap.copy(bitmap.config, bitmap.isMutable)
bitmap2.setDensity(bitmap.getDensity() * 2);
imageView.setBitmap(bitmap);

Bitmap bitmap3 = bitmap.copy(bitmap.config, bitmap.isMutable)
bitmap3.setDensity(bitmap.getDensity() / 2);
imageView.setBitmap(bitmap);

Bitmap的density默认是设备的density,但是可以修改,当Bitmap被绘制到Canvas时,会根据density做出合适的缩放效果:

2024-02-22-12-23-20-image.png

mipmap(扩展内容)

mipmap 是一种很早就有的技术了(纹理映射技术),android 中的 mipmap 技术主要为了应对图片大小缩放的处理,在android 中我们提供一个 bitmap 图片,由于应用的需要(比如缩放动画),可能对这个 bitmap 进行各种比例的缩小,为了提高缩小的速度和图片的质量,android 通过 mipmap 技术提前对按缩小层级生成图片预先存储在内存中,这样就提高了图片渲染的速度和质量。在API中通过 Bitmap 的 setHasMipMap (boolean hasMipMap) 方法可以让系统渲染器尝试开启 Bitmap 的 mipmap 技术。但是这个方法只能建议系统开启这个功能,至于是否正真开启,还是由系统决定。

res 目录下面 mipmap 和 drawable 的区别也就是上面这个设置是否开启的区别:

  • mipmap 目录下的图片默认 setHasMipMap 为 true,
  • drawable 默认 setHasMipMap 为 false

google 建议大家只把 app 的启动图标放在 mipmap 目录中,其他图片资源仍然放在 drawable 下面。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
android Bitmap用法总结 Bitmap用法总结 1、Drawable → Bitmap public static Bitmap drawableToBitmap(Drawable drawable) { Bitmap bitmap = Bitmap .createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); // canvas.setBitmap(bitmap); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); drawable.draw(canvas); return bitmap; } 2、从资源中获取Bitmap Resources res=getResources(); Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic); 3、Bitmap → byte[] private byte[] Bitmap2Bytes(Bitmap bm){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, baos); return baos.toByteArray(); } 4、byte[] → Bitmap private Bitmap Bytes2Bimap(byte[] b){ if(b.length!=0){ return BitmapFactory.decodeByteArray(b, 0, b.length); } else { return null; } } 5、保存bitmap static boolean saveBitmap2file(Bitmap bmp,String filename){ CompressFormat format= Bitmap.CompressFormat.JPEG; int quality = 100; OutputStream stream = null; try { stream = new FileOutputStream("/sdcard/" + filename); } catch (FileNotFoundException e) { // TODO Auto-generated catch block Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. e.printStackTrace(); } return bmp.compress(format, quality, stream); } 6、将图片按自己的要求缩放 // 图片源 Bitmap bm = BitmapFactory.decodeStream(getResources() .openRawResource(R.drawable.dog)); // 获得图片的宽高 int width = bm.getWidth(); int height = bm.getHeight(); // 设置想要的大小 int newWidth = 320; int newHeight = 480; // 计算缩放比例 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 取得想要缩放的matrix参数 Matrix matrix = new Matrix(); matrix.postScale(scaleWidth, scaleHeight); // 得到新的图片 Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true); // 放在画布上 canvas.drawBitmap(newbm, 0, 0, paint); 相关知识链接:http://www.eoeandroid.com/thread-3162-1-1.html 7、bitmap的用法小结 BitmapFactory.Options option = new BitmapFactory.Options(); option.inSampleSize = 2; //将图片设为原来宽高的1/2,防止内存溢出 Bitmap bm = BitmapFactory.decodeFile("",option);//文件流 URL url = new URL(""); InputStream is = url.openStream(); Bitmap bm = BitmapFactory.decodeStream(is); android:scaleType: android:scaleType是控制图片如何resized/moved来匹对ImageView的size。ImageView.ScaleType / android:scaleType值的意义区别: CENTER /center 按图片的原来size居中显示,当图片长/宽超过View的长/宽,则截取图片的居中部分 显示 CENTER_CROP / centerCrop 按比例扩大图片的size居中显示,使得图片长(宽)等于或大于View的长 (宽) CENTER_INSIDE / centerInside 将图片的内容完整居中显示,通过按比例缩小或原来的size使得图片 长/宽等于或小于View的长/宽 Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. FIT_CENTER / fitCenter 把图片按比例扩大/缩小到View的宽度,居中显示 FIT_END / fitEnd 把图片按比例扩大/缩小到View的宽度,显示在View的下部分位置 FIT_START / fitStart 把图片按比例扩大/缩小到View的宽度,显示在View的上部分位置 FIT_XY / fitXY 把图片 不按比例 扩大/缩小到View的大小显示 MATRIX / matrix 用矩阵来绘制,动态缩小放大图片来显示。 //放大缩小图片 public static Bitmap zoomBitmap(Bitmap bitmap,int w,int h){ int width = bitmap.getWidth(); int height = bitmap.getHeight(); Matrix matrix = new Matrix(); float scaleWidht = ((float)w / width); float scaleHeight = ((float)h / height); matrix.postScale(scaleWidht, scaleHeight); Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); return newbmp; } //将Drawable转化为Bitmap public static Bitmap drawableToBitmap(Drawable drawable){ int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); Bitmap bitmap = Bitmap.createBitmap(width, height, drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0,0,width,height); drawable.draw(canvas); return bitmap; Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. } //获得圆角图片的方法 public static Bitmap getRoundedCornerBitmap(Bitmap bitmap,float roundPx){ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap .getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } //获得带倒影的图片方法 public static Bitmap createReflectionImageWithOrigin(Bitmap bitmap){ final int reflectionGap = 4; int width = bitmap.getWidth(); int height = bitmap.getHeight(); Matrix matrix = new Matrix(); matrix.preScale(1, -1); Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, height/2, width, height/2, matrix, false); Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height/2), Config.ARGB_8888); Canvas canvas = new Canvas(bitmapWithReflection); canvas.drawBitmap(bitmap, 0, 0, null); Paint deafalutPaint = new Paint(); Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. canvas.drawRect(0, height,width,height + reflectionGap, deafalutPaint); canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null); Paint paint = new Paint(); LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0, bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff, TileMode.CLAMP); paint.setShader(shader); // Set the Transfer mode to be porter duff and destination in paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); // Draw a rectangle using the paint with our linear gradient canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint); return bitmapWithReflection; } }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值