MIUI 6的毛玻璃效果的技術實現(實時模糊)


說說MIUI 6的毛玻璃效果的技術實現。
 
很久以前我們的文件夾打開和最近任務等幾個地方就使用了毛玻璃效果,在技術上講就是背景模糊。應該是比iOS 7的使用要早很多。不過那時候我們使用的是先對背景截圖,再將其模糊處理,然后作為app的背景圖,所以是一張靜止的圖片,當背景內容發生變化時模糊的圖片並不會隨之變化,因此比較生硬。而iOS 7的背景模糊則是實時的,當背景變化的時候,模糊內容也會實時的變化。這就把我們給比下去了。於是我們也不能閑着,既然做了,那就做好。然后我就開始做實時的背景模糊。
 
如果沿用之前的方式,先截圖,再模糊,由於這個過程遠遠大於一幀的時間16.7毫秒。因此就算不停的更新背景,雖然也能呈現出變化的模糊背景,但會很卡頓,不流暢,不自然,資源消耗大。
 
於是考慮更為自然的實現方式:在Window上添加標記,SurfaceFlinger合並層的時候對其后面的內容用OpenGL ES進行模糊處理。
這種方式沒有截圖的過程,繪制的時候用OpenGL ES繪制,加上一些優化,最終輕松達到60FPS的滿幀率。
 
下面說說代碼的主要過程和一些關鍵點。
  1. 需要背景模糊的Window給自己添加一個標記:FLAG_BLUR_BEHIND,這個標記是Android SDK提供的,因為Android的早期版本是有支持背景模糊的,不過由於他們不是用的OpenGL ES實時繪制,性能比較差,於是后來把這個功能給廢棄了。我們便順理成章的又把這個標記給用起來。第三方應用也完全可以使用這個標記在MIUI 6用上這個特性。

  2. 在WindowManagerService這邊檢測到Window有這個標記后,將標記傳給SurfaceFlinger中Window對應的Layer。

  3. Layer收到背景模糊標記后,分配一個Texture。

  4. 在SurfaceFlinger合成層的方法doComposeSurfaces中,把背景模糊的層后面的內容用OpenGL ES以模糊的方式繪制到之前分配的Texture中,於是構建好了模糊的內容。在接下來繪制這個層的時候先繪制含有模糊內容的Texture。
整個過程大致就是這樣。
另外,由於在大部分情況下SurfaceFlinger合成層是使用的Hardware Composer來合成的,並不是OpenGL ES,因此我們需要檢測發現有背景模糊層的時候指明不使用Hardware Composer。
 
模糊算法的主要耗時點是很大的采樣次數。我們想了一些辦法來優化性能:
    1. 將圖片先縮小,再模糊,繪制的時候再放大。由於先縮小再放大也是一種模糊方法,因此控制得好的話並沒有什么問題。但防止縮小太厲害會導致背景移動的時候畫面閃爍,我們會逐級縮小。(GL_TEXTURE_EXTERNAL_OES不支持mipmap)

    2. 模糊的主要實現過程使用OpenGL ES Fragment Shader來實現,先橫向計算一遍,再縱向計算一遍,這樣處理則每個像素的采樣次數從n*n變成了n*2(n是模糊直徑)。事實上我們在背景模糊的Layer里分配了兩個Texture,循環使用。

    3. 采樣取像素值的時候並不需要每個像素取一次,而是利用OpenGL ES中紋理采樣的線性過濾特性,在兩個像素中間取值,返回的是兩個像素的平均值,這樣可以讓采樣次數減半,效果不變。(真實代碼中並不是完全中間,而是根據權重計算位置)

    4. 如果上一幀已經產生了模糊的Texture,而這一幀背景又沒有變化,則直接使用上一幀的就行了,不需要再做模糊處理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM