原創(chuàng)作者:AchillesL
若轉(zhuǎn)載文章库糠,請(qǐng)?jiān)诿黠@的位置標(biāo)明文章出處
在Android開發(fā)中司忱,只要涉及到圖片處理北秽,基本離不開Bitmap蜡峰。 作為開發(fā)者了袁,我們平時(shí)和Bitmap打交道也是非常多的。本章中湿颅,筆者打算總結(jié)一下這方面的內(nèi)容载绿。若發(fā)現(xiàn)錯(cuò)漏之處,請(qǐng)及時(shí)提出油航。
0 本章內(nèi)容
- 一個(gè)Bitmap對(duì)象占多大內(nèi)存空間
- 解決大圖加載OOM的幾種方式
- 圖片的縮放崭庸、旋轉(zhuǎn)、傾斜等處理
- 圖片的切割處理
- 顏色矩陣ColorMatrix
- 使用BitmapRegionDecoder加載高清巨圖
1 一個(gè)Bitmap對(duì)象占多大內(nèi)存空間
1.1 解碼格式
筆者曾經(jīng)在面試時(shí),也曾經(jīng)遇到過這個(gè)問題:一個(gè)Bitmap對(duì)象怕享, 究竟占用多大內(nèi)存空間执赡?
一般來講,Bitmap的占用的內(nèi)存空間熬粗,有下面的計(jì)算方法:
bitmap的內(nèi)存空間 = 圖片寬度 * 圖片高度 * 單位像素所占字節(jié)數(shù)(Byte)
圖片寬度和高度比較好理解搀玖,單位像素所占字節(jié)數(shù)是個(gè)什么玩意,難道每個(gè)像素的字節(jié)數(shù)會(huì)不一樣嗎驻呐?
確實(shí)會(huì)不一樣灌诅,單位像素字節(jié)數(shù)與解析Bitmap時(shí)解碼的格式有關(guān),Android 系統(tǒng)支持四種像素解碼格式含末,如下圖所示:
像素格式 | 單位像素字節(jié)數(shù) | 備注 |
---|---|---|
ARGB_8888 | 4 | ARGB四個(gè)通道猜拾,每個(gè)通道占8位,即每個(gè)像素點(diǎn)占32位佣盒,4個(gè)字節(jié) |
RGB_565 | 2 | RGB三個(gè)通道挎袜,分別占5、6肥惭、5位盯仪,即每個(gè)像素點(diǎn)占16位,2個(gè)字節(jié) |
ARGB_4444 | 2 | ARGB四個(gè)通道蜜葱,每個(gè)通道占4位全景,即每個(gè)像素點(diǎn)占16位,2個(gè)字節(jié) |
ALPHA_8 | 1 | 只有透明度通道牵囤,占8位爸黄,1個(gè)字節(jié) |
ALPHA_8只有透明度通道,只能用于一些特殊場(chǎng)景揭鳞。
ARGB_4444由于質(zhì)量太差炕贵,已經(jīng)不建議使用,而且在KITKAT(Android4.4)及其以上版本野崇,使用ARGB_4444參數(shù)會(huì)直接在代碼中被忽略称开,用ARGB_8888替代。
我們常用的只有RGB_565和ARGB_8888乓梨。
1.2 ARBG_8888 PK RGB_565
使用RGB_565格式解碼后钥弯,圖片每像素點(diǎn)占2字節(jié),而ARGB_8888解碼則每像素點(diǎn)占4字節(jié)督禽。按道理,使用RGB_565格式解碼應(yīng)該比RGB_8888解碼省一半內(nèi)存总处。接下來狈惫,我們做個(gè)實(shí)驗(yàn)來驗(yàn)證一下:
筆者手上有一張圖,長(zhǎng)400像素,寬300像素胧谈,jpg格式忆肾。我們把它放到 assets 目錄中,然后編寫代碼菱肖,查看一下它的內(nèi)存占用情況客冈。
圖片的占用內(nèi)存,我們可以用Bitmap的getByteCount方法得到稳强,在運(yùn)行程序前场仲,我們先按照公式算出該圖內(nèi)存占用是多少。由于Android默認(rèn)采用ARGB_8888格式解碼退疫,即一個(gè)像素點(diǎn)占4個(gè)字節(jié)渠缕,因此:
該圖占用內(nèi)存 = 400 * 300 * 4 = 480000 Byte
運(yùn)行程序,發(fā)現(xiàn)結(jié)果和我們理論值是一模一樣的:
第二步褒繁,我們驗(yàn)證一下采用RGB_565格式進(jìn)行解碼亦鳞,看圖片內(nèi)存占用如何。我們可以通過BitmapFactory.Options類的inPreferredConfig屬性棒坏,指定圖片解碼格式燕差,代碼如下:
運(yùn)行程序,發(fā)現(xiàn)果然比用ARGB_8888解碼少占用一半的內(nèi)存坝冕。
但需要注意的是徒探,使用RGB_565解碼,不一定總比ARGB_8888解碼占用內(nèi)存更小徽诲,如果你對(duì)png格式的圖片采用RGB_565格式解碼刹帕,得到的結(jié)果很可能是和使用ARGB_8888的結(jié)果是一致。這是由于:
如果inPreferredConfig為null或者在解碼時(shí)無法滿足此參數(shù)指定的顏色模式谎替,解碼器會(huì)自動(dòng)根據(jù)原始圖片的特征以及當(dāng)前設(shè)備的屏幕位深偷溺,選取合適的顏色模式來解碼,例如钱贯,如果圖片中包含透明度挫掏,那么對(duì)該圖片解碼時(shí)使用的配置就需要支持透明度,默認(rèn)會(huì)使用ARGB_8888來解碼秩命。
也就是說**inPreferredConfig指定的配置并非是一個(gè)強(qiáng)制選項(xiàng)尉共,而是建議的(preferred)選項(xiàng),Android在實(shí)際解碼時(shí)會(huì)參考此參數(shù)的配置弃锐,但如果此配置不滿足袄友,Android會(huì)重新選取一個(gè)合適的配置來對(duì)圖片進(jìn)行解碼。 **
最后霹菊,我們有以下總結(jié):
對(duì)于jpg格式圖片剧蚣,使用RGB_565格式進(jìn)行解碼,圖片占用的內(nèi)存會(huì)更少
1.3 不同資源目錄下,圖片內(nèi)存占用情況
細(xì)心的讀者可能已經(jīng)留意到鸠按,上述的實(shí)驗(yàn)過程礼搁,圖片是存放在Android的assets 目錄下,這是筆者為了避免圖片存放在不同drawable目錄下產(chǎn)生占用內(nèi)存不同的影響目尖。接下來馒吴,我們將圖片放在不同的drawable目錄并分別查看內(nèi)存的占用情況。
圖片放在drawable-hdpi目錄時(shí)瑟曲,內(nèi)存占用1920000字節(jié):
圖片放在drawable-xhdpi目錄時(shí)饮戳,內(nèi)存占用1080000字節(jié):
圖片放在drawable-xxhdpi目錄時(shí),內(nèi)存占用480000字節(jié):
我們從實(shí)驗(yàn)結(jié)果發(fā)現(xiàn)测蹲,除了放在 drawable-xxhdpi目錄下莹捡,否則圖片內(nèi)存占用都要比上述計(jì)算的理論值要大很多,這是為什么呢扣甲?
首先我們知道篮赢,Android中drawable系列目錄的存在,如:drawable-ldpi琉挖、drawable-mdpi启泣、drawable-hdpi、drawable-xhdpi示辈、drawable-xxhdpi等寥茫,是為了兼容適配不同手機(jī)屏幕的屏幕密度。
屏幕密度與Drawable系列目錄的對(duì)應(yīng)關(guān)系如下:
目錄 | 屏幕密度 |
---|---|
drawable-ldpi | 120dpi |
drawable-mdpi | 160dpi |
drawable-hdpi | 240dpi |
drawable-xhdpi | 320dpi |
drawable-xxhdpi | 480dpi |
當(dāng)我們使用decodeResource方法讀取drawable目錄下面的圖片時(shí)矾麻,會(huì)根據(jù)手機(jī)的屏幕密度纱耻,到對(duì)應(yīng)的文件夾中查找該圖片。若該圖片存在于其他目錄下险耀,系統(tǒng)先對(duì)該圖片進(jìn)行縮放處理弄喘,再顯示。
也就是說甩牺,資源圖片需要根據(jù)手機(jī)屏幕密度的值來存放到不同的drawable目錄中蘑志,圖片的顯示才是正常的。
對(duì)于我們的例子贬派,由于筆者實(shí)驗(yàn)的機(jī)器是小米5急但,該手機(jī)的屏幕密度是480dpi,因此圖片需要放在drawable-xxhpid目錄下搞乏。如果圖片放在其他目錄如hdpi時(shí)波桩,系統(tǒng)會(huì)認(rèn)為該圖是使用在hdpi密度的屏幕上,而實(shí)際的屏幕密度卻比hdpi更高请敦,此時(shí)系統(tǒng)將會(huì)對(duì)圖片進(jìn)行放大處理镐躲,這樣避免了不同屏幕密度影響圖片顯示效果柏卤。由于存在系統(tǒng)對(duì)圖片進(jìn)行縮放操作,也就解釋了為什么實(shí)際的內(nèi)存占用比理論值要大匀油。
當(dāng)圖片存放在drawable目錄時(shí),占用的內(nèi)存空間有以下計(jì)算公式:
scale = 設(shè)備的屏幕密度 / drawable目錄設(shè)定的屏幕密度
圖片占用內(nèi)存 = int(圖片長(zhǎng)度 * scale + 0.5) * int(圖片寬度 * scale + 0.5) * 單位像素字節(jié)數(shù)
因此勾笆,我們的例子中敌蚜,圖片放入hdpi文件夾時(shí)所占內(nèi)存為:
scale = 480 / 240 = 2;
內(nèi)存 = int(400 * 2 + 0.5) * int(300 * 2 + 0.5) * 4 = 1920000
圖片放入xhdpi文件夾時(shí)所占內(nèi)存:
scale = 480 / 320 = 1.5;
內(nèi)存 = int(400 * 1.5 + 0.5) * int(300 * 1.5 + 0.5) * 4 = 1080000
這樣,十分完美地解釋了上述的實(shí)驗(yàn)結(jié)果窝爪。另外弛车,關(guān)于圖片在內(nèi)存中占用空間,筆者也推薦大家閱讀這篇由騰訊Bugly出品的文章:Android坑檔案:你的Bitmap究竟占多大內(nèi)存蒲每?
2 解決大圖加載OOM的幾種方式
大圖加載避免OOM纷跛,算是老話題了,網(wǎng)上的資料很多邀杏,在此筆者只介紹幾種方法贫奠。OOM名為內(nèi)存溢出,在避免內(nèi)存溢出前望蜡,我們先搞清楚一個(gè)問題:為什么會(huì)發(fā)生內(nèi)存溢出唤崭?
2.1 largeHeap模式
這要從Android的內(nèi)存管理說起,Android系統(tǒng)的手機(jī)在系統(tǒng)底層指定了堆內(nèi)存的上限值脖律,我們的程序在申請(qǐng)內(nèi)存空間時(shí)谢肾,為了確保能夠成功申請(qǐng)到內(nèi)存空間,應(yīng)該保證當(dāng)前已分配的內(nèi)存加上當(dāng)前需要分配的內(nèi)存值的總大小不能超過當(dāng)前堆的最大內(nèi)存值(手機(jī)廠商會(huì)根據(jù)手機(jī)的配置情況來對(duì)其進(jìn)行調(diào)整)小泉。
如果解析后圖片的大小加上目前已分配的堆內(nèi)存大小超過堆內(nèi)存最大值芦疏,系統(tǒng)先會(huì)執(zhí)行一遍gc操作,若gc后仍然超過堆內(nèi)存最大值微姊,這時(shí)候?qū)?huì)拋出異常酸茴,這是就是發(fā)生了我們常說的OOM。
查看手機(jī)的堆內(nèi)存大小柒桑,我們有兩種方式:
通過代碼查看:
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
Log.d("AchillesL","size: " + activityManager.getMemoryClass());
OR弊决,通過adb指令查看:
adb shell getprop|grep heapgrowthlimit
筆者的測(cè)試機(jī)是小米5,查到堆內(nèi)存最大值為256M魁淳。
可不可以提高堆內(nèi)存最大值來避免OOM呢飘诗?答案是可以的,我們只需要在AndroidManifest.xml中的Application節(jié)點(diǎn)中聲明android:largeHeap="true"
標(biāo)識(shí)界逛,即可以分配到更大的堆內(nèi)存空間昆稿。
查看能分配到最大的堆內(nèi)存空間,我們有兩種方式:
通過代碼查看:
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
Log.d("AchillesL","size: " + activityManager.getLargeMemoryClass());
OR息拜,通過adb指令查看:
adb shell getprop|grep dalvik.vm.heapsize
需要注意的是溉潭,采用設(shè)置largeHeap的方式净响,提高了分配到堆內(nèi)存的上限,但這僅僅延遲了OOM的方式時(shí)期喳瓣,治標(biāo)不治本馋贤。關(guān)鍵還需要我們從本身的代碼入手,從根本上解決問題畏陕。
如果我們?cè)跈C(jī)頂盒上開發(fā)播放器應(yīng)用配乓,顯示的圖片都較大,而且非常消耗內(nèi)存惠毁,一般在應(yīng)用開發(fā)完成并且測(cè)試通過發(fā)布時(shí)犹芹,最后再加上android:largeHeap="true"
標(biāo)記。
2.2 采用合適的圖片解碼格式
很多情況下鞠绰,網(wǎng)絡(luò)下載的圖片都使用jpg格式腰埂。對(duì)于jpg圖片,使用RGB_565格式進(jìn)行解碼比ARGB_8888解碼會(huì)節(jié)省更多內(nèi)存蜈膨。
設(shè)置圖片的解碼格式屿笼,可以通過BitmapFactory.Options類的inPreferredConfig屬性來指定。代碼如下所示:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic,options);
總結(jié):
如果不需要alpha通道丈挟,特別是資源本身為jpg格式的情況下刁卜,用RGB_565格式解碼更加節(jié)省內(nèi)存。
2.3 設(shè)置采樣率
設(shè)置采樣率曙咽,這種方式我們?cè)偈煜げ贿^了蛔趴。解析圖片時(shí),通過設(shè)置采樣率減少加載后圖片的尺寸例朱,達(dá)到減少圖片內(nèi)存占用的目的孝情。該方法一般分為三步:
設(shè)置BitmapFactory.Options類的inJustDecodeBounds屬性為true,表示加載圖片時(shí)不分配圖片的內(nèi)存空間洒嗤,僅僅計(jì)算出原始圖片的寬度和高度箫荡,即options.width和options.height。
得到原始圖片的寬度渔隶、高度羔挡,以及圖片需要展示的寬度、高度间唉,計(jì)算出合適的采樣率inSampleSize绞灼。
設(shè)置BitmapFactory.Options類的inJustDecodeBounds屬性為false,并設(shè)置圖片采樣率inSampleSize呈野,然后加載圖片低矮。
其中,inSampleSize的值必須大于1被冒。若inSampleSize的值為2军掂,表示目標(biāo)圖片的寬轮蜕、高都為原圖的 1 / 2,此時(shí)內(nèi)存占用只有原圖的 1 / 4蝗锥。
代碼如下:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(),R.drawable.pic,options);
//目標(biāo)圖片寬度為200像素
int scale = options.outWidth / 200;
scale = scale > 1 ? scale: 1;
options.inJustDecodeBounds = false;
options.inSampleSize = scale;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic,options);
Log.d("AchillesL","width: " + bitmap.getWidth() + " height: " + bitmap.getHeight());
另外跃洛,最新的官方文檔中指出,采樣率inSampleSize 的值终议,應(yīng)當(dāng)總是2的指數(shù)税课,比如1,2痊剖,4,8垒玲,16等陆馁,若傳遞給系統(tǒng)的值不是2的指數(shù),那么系統(tǒng)會(huì)向下取整并選擇一個(gè)最接近2的指數(shù)來代替合愈。因此叮贩,在大部分情況下設(shè)置圖片的采樣率,只能達(dá)到節(jié)省內(nèi)存的目的佛析,并不能精確地控制圖片的尺寸益老。
3 圖片的縮放、旋轉(zhuǎn)寸莫、傾斜等處理
本節(jié)主要總結(jié)Matrix對(duì)圖片進(jìn)行處理的方法捺萌。Matrix操作,總共分為translate(平移)膘茎,scale(縮放)桃纯,rotate(旋轉(zhuǎn))和skew(傾斜)四種。
3.1 縮放
上一節(jié)我們提到披坏,設(shè)置圖片的采樣率态坦,可以節(jié)省內(nèi)存,但不能精確控制圖片尺寸棒拂。如果我們需要圖片在節(jié)省內(nèi)存的同時(shí)伞梯,再使用具體的數(shù)值來展示,則需要我們另外處理帚屉。
使用Matrix類的postScale方法可以很輕松地完成圖片的縮放谜诫,代碼如下:
//獲取原始圖片
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic);
Log.d("AchillesL","origin width: " + bitmap.getWidth() + " origin height: " + bitmap.getHeight());
Matrix matrix = new Matrix();
//將圖片寬度縮放到150像素
float scale = (float) (150.0 / bitmap.getWidth());
//寬高等比例縮放
matrix.postScale(scale,scale);
Bitmap resultBitmap = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
Log.d("AchillesL","width: " + resultBitmap.getWidth() + " height: " + resultBitmap.getHeight());
效果圖:
一般來講,我們需要將一張大圖以具體的小尺寸顯示時(shí)涮阔,一般先設(shè)置采樣率猜绣,進(jìn)行“粗略”縮小達(dá)到目標(biāo)尺寸,再利用Matrix 進(jìn)行精確的尺寸控制敬特。
另外掰邢,除了Matrix牺陶,也可以使用Bitmap類的createScaledBitmap方法進(jìn)行縮放處理。
3.2 旋轉(zhuǎn)
借助Matrix的postRotate方法可以使圖片旋轉(zhuǎn)一定角度辣之。
代碼如下:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic);
Matrix matrix = new Matrix();
matrix.postRotate(30);
Bitmap resultBitmap = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
imageView.setImageBitmap(resultBitmap);
效果圖:
3.3 傾斜
借助Matrix的setSkew方法可以使圖片產(chǎn)生傾斜效果掰伸。
代碼如下:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic);
Matrix matrix = new Matrix();
matrix.setSkew((float) 0.5,0);
Bitmap resultBitmap = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
imageView.setImageBitmap(resultBitmap);
效果圖:
3.4 preXXX、postXXX怀估、setXXX等幾個(gè)方法的區(qū)別
我們注意到狮鸭,Matrix的四種基本操作:translate(平移),scale(縮放)多搀,rotate(旋轉(zhuǎn))和skew(傾斜)都提供了pre歧蕉、post、set等三個(gè)方法康铭。那它們有什么區(qū)別呢惯退?
set是直接設(shè)置Matrix的值,每一次set从藤,整個(gè)Matrix的數(shù)組都會(huì)重置催跪。例如:
Matrix matrix = new Matrix();
matrix.setSkew((float) 0.5,0);
matrix.setRotate(30);
我們只能看到旋轉(zhuǎn)的效果,看不到傾斜效果夷野。
post是后乘懊蒸,當(dāng)前的矩陣乘以參數(shù)給出的矩陣∶跎Γ可以連續(xù)多次使用post骑丸,來完成所需的整個(gè)變換。例如:
Matrix matrix = new Matrix();
matrix.postRotate(30);
matrix.postTranslate(100,100);
表示把圖片先旋轉(zhuǎn)30度妒貌,再平移到坐標(biāo)(100,100)的地方者娱。
pre是前乘,參數(shù)給出的矩陣乘以當(dāng)前的矩陣苏揣。所以操作是在當(dāng)前矩陣的最前面發(fā)生的黄鳍。例如:
Matrix matrix = new Matrix();
matrix.setTranslate(100,100);
matrix.preRotate(30)
表示把圖片先旋轉(zhuǎn)30度,再平移到坐標(biāo)(100,100)的地方平匈。
4 圖片的切割處理
使用Bitmap時(shí)框沟,對(duì)圖片進(jìn)行切割是經(jīng)常遇到的事情,本節(jié)將總結(jié)將Bitmap切割為矩形增炭、圓角忍燥、圓形等三種情況。
4.1 矩形
Bitmap的矩形切割非常簡(jiǎn)單隙姿,直接使用Bitmap類的createBitmap方法即可梅垄,代碼如下:
//獲取原始圖片
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic,options);
/*從原圖以0,0位坐標(biāo)原點(diǎn),切割一個(gè)寬100输玷,高100的圖片*/
Bitmap resultBitmap = Bitmap.createBitmap(bitmap,0,0,100,100);
Log.d("AchillesL","width: " + resultBitmap.getWidth() + " height: " + resultBitmap.getHeight());
通過這個(gè)API队丝,實(shí)現(xiàn)一個(gè)拼圖游戲也是非常簡(jiǎn)單的事情靡馁,如下圖所示。(如何實(shí)現(xiàn)拼圖游戲机久,筆者將會(huì)在后續(xù)的文章中講述)
4.2 圓角
實(shí)現(xiàn)圖片的圓角并沒有現(xiàn)成的API臭墨,需要借助Xfermode和PorterDuffXfermode來處理,我們通過把圓角矩形套在原Bitmap上取交集得到圓角Bitmap膘盖。
代碼如下所示:
//獲取原始圖片
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic,options);
// 準(zhǔn)備畫筆
Paint paint = new Paint();
paint.setAntiAlias(true);
// 準(zhǔn)備裁剪的矩陣
Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
RectF rectF = new RectF(rect);
Bitmap roundBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap
.Config.ARGB_8888);
Canvas canvas = new Canvas(roundBitmap);
// 圓角矩陣胧弛,圓角半徑為20像素
canvas.drawRoundRect(rectF, 20 , 20, paint);
// 關(guān)鍵代碼,關(guān)于Xfermode和SRC_IN請(qǐng)自行查閱
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
imageView.setImageBitmap(roundBitmap);
效果圖:
4.3 圓形
圓形切割和上面的圓角切割原理相同侠畔,區(qū)別是將圓形套在圖片上并取交集结缚。
代碼如下所示:
//獲取原始圖片
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic,options);
int min = bitmap.getWidth() > bitmap.getHeight() ?
bitmap.getHeight() : bitmap.getWidth();
Paint paint = new Paint();
paint.setAntiAlias(true);
Bitmap circleBitmap = Bitmap.createBitmap(min, min,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(circleBitmap);
// 圓形
canvas.drawCircle(min / 2, min / 2, min / 2, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// 居中顯示
int left = - (bitmap.getWidth() - min) / 2;
int top = - (bitmap.getHeight() - min) / 2;
canvas.drawBitmap(bitmap, left, top, paint);
imageView.setImageBitmap(circleBitmap);
效果圖:
采用Canvas與PorterDuffXfermode的方式,我們可以繪制任何形狀的圖片软棺。
5 顏色矩陣ColorMatrix
在Android中掺冠,對(duì)圖像進(jìn)行顏色方面的處理,如黑白老照片码党、泛黃舊照片、高對(duì)比度斥黑、低飽和度等效果揖盘,都可以通過使用顏色矩陣ColorMatrix來實(shí)現(xiàn)。
灰階效果:
Bitmap originBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic);
Bitmap grayBitmap = Bitmap.createBitmap(originBitmap.getWidth(),
originBitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(grayBitmap);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
// 設(shè)置飽和度為0锌奴,實(shí)現(xiàn)了灰階效果
colorMatrix.setSaturation(0);
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(colorMatrixColorFilter);
canvas.drawBitmap(originBitmap, 0, 0, paint);
imageView.setImageBitmap(grayBitmap);
效果圖:
6 使用BitmapRegionDecoder加載高清巨圖
前面我們講到使用采樣率來避免大圖加載OOM兽狭,但開發(fā)中還可能會(huì)遇到一種場(chǎng)景,就是加載長(zhǎng)圖或世界地圖鹿蜀。這種圖片體積大箕慧,而且不能通過設(shè)置采樣率來加載顯示,否則將會(huì)丟失圖片細(xì)節(jié)茴恰。
一般加載高清巨圖颠焦,我們使用BitmapRegionDecoder來處理。通過該類往枣,我們可以加載局部的圖片伐庭。
BitmapRegionDecoder的用法很簡(jiǎn)單:
public static BitmapRegionDecoder newInstance(byte[] data, int offset,
int length, boolean isShareable) throws IOException {
}
public static BitmapRegionDecoder newInstance(
FileDescriptor fd, boolean isShareable) throws IOException {
}
public static BitmapRegionDecoder newInstance(InputStream is,
boolean isShareable) throws IOException {
}
public static BitmapRegionDecoder newInstance(String pathName,
boolean isShareable) throws IOException {
}
我們以加載世界地圖為例,通過BitmapRegionDecoder類加載世界地圖的左上角部分并顯示分冈,代碼如下:
BitmapRegionDecoder bitmapRegionDecoder = null;
try {
bitmapRegionDecoder = BitmapRegionDecoder.newInstance(getAssets().open("world.jpg"),
true);
} catch (IOException e) {
e.printStackTrace();
}
int screenWidth = getResources().getDisplayMetrics().widthPixels;
int screenHeight = getResources().getDisplayMetrics().heightPixels;
/*以手機(jī)屏幕寬高生成一個(gè)矩形區(qū)域*/
Rect rect = new Rect(0,0,screenWidth,screenHeight);
BitmapFactory.Options options = new BitmapFactory.Options();
/*設(shè)置RGB_565格式*/
options.inPreferredConfig = Bitmap.Config.RGB_565;
/*加載部分圖片*/
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(rect,options);
imageView.setImageBitmap(bitmap);
效果圖:
另外圾另,我們可以配合滑動(dòng)手勢(shì),動(dòng)態(tài)加載不同區(qū)域的圖片雕沉,實(shí)現(xiàn)一個(gè)可以瀏覽大圖的APP集乔,筆者在后續(xù)的文章將會(huì)講到。
7 結(jié)束語
本文總結(jié)了關(guān)于Bitmap的知識(shí)點(diǎn)坡椒,如果發(fā)現(xiàn)錯(cuò)漏之處扰路,請(qǐng)留言回復(fù)提出尤溜。