聊聊Bitmap的那些事兒

原創(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_565ARGB_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ù)提出尤溜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市幼衰,隨后出現(xiàn)的幾起案子靴跛,更是在濱河造成了極大的恐慌,老刑警劉巖渡嚣,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梢睛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡识椰,警方通過查閱死者的電腦和手機(jī)绝葡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腹鹉,“玉大人藏畅,你說我怎么就攤上這事」χ洌” “怎么了愉阎?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)力奋。 經(jīng)常有香客問我榜旦,道長(zhǎng),這世上最難降的妖魔是什么景殷? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任溅呢,我火速辦了婚禮,結(jié)果婚禮上猿挚,老公的妹妹穿的比我還像新娘咐旧。我一直安慰自己,他們只是感情好绩蜻,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布铣墨。 她就那樣靜靜地躺著,像睡著了一般办绝。 火紅的嫁衣襯著肌膚如雪踏兜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天八秃,我揣著相機(jī)與錄音碱妆,去河邊找鬼。 笑死昔驱,一個(gè)胖子當(dāng)著我的面吹牛疹尾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼纳本,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼窍蓝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起繁成,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤吓笙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后巾腕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體面睛,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年尊搬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叁鉴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡佛寿,死狀恐怖艰猬,靈堂內(nèi)的尸體忽然破棺而出容客,到底是詐尸還是另有隱情浦译,我是刑警寧澤裆站,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站弹渔,受9級(jí)特大地震影響胳施,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捞附,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望您没。 院中可真熱鬧鸟召,春花似錦、人聲如沸氨鹏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仆抵。三九已至跟继,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镣丑,已是汗流浹背舔糖。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留莺匠,地道東北人金吗。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親摇庙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子旱物,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 2021期待與你一起共事,點(diǎn)擊查看崗位[http://www.reibang.com/p/6f4d67fa406...
    閑庭閱讀 16,609評(píng)論 0 75
  • 參考資料 目錄 Bitmap BitmapFactory Bitmap加載方法 Bitmap | Drawable...
    玄策閱讀 2,756評(píng)論 0 7
  • 一卫袒、Bitmap 內(nèi)存回收 從3.0開始宵呛,Bitmap 像素?cái)?shù)據(jù)和 Bitmap 對(duì)象一起存放在 Dalvik 堆...
    秀花123閱讀 1,819評(píng)論 1 7
  • 一直以來Bitmap都是開發(fā)中很棘手的問題,這個(gè)問題就是傳說中的OOM(java.lang.OutofMemory...
    M悇芐冋憶閱讀 4,702評(píng)論 0 11
  • 01 在我們已知的世界里夕凝,地球是唯一可以生存的星球宝穗,這個(gè)星球上有生命。 不管是動(dòng)物還是植物迹冤,都會(huì)經(jīng)歷出生讽营,成長(zhǎng),死...
    Soul麥芽閱讀 2,980評(píng)論 32 61