GPUImage作為iOS相當老牌的圖片處理三方庫已經有些日子了(2013年發布第一個版本),至今甚至感覺要離我們慢慢遠去(2015年更新了最后一個release)。可能現在分享這個稍微有點晚,再加上落影大神早已發布過此類文章,但是還是想從自己的角度來分享一下對其的理解和想法。
本文集所有內容皆為原創,嚴禁轉載。
? ? ? ? 我在2015年底因為手頭項目需求原因接觸到GPUImage,在此之前在上海一家十分傳統的金融互聯網公司做著我的第一份iOS開發工作,面對著每天的TableView、CollectionView顯示著異步獲取下來的各式數據做著各種屏幕自適應,至今我還特別討厭iPad翻轉自適應,不過現在有諸如Masonry、SnapKit等相當好用的框架后好像容易解決一些。
? ? ? ? 機緣巧合下來到杭州,完全一個人的開發工作,邊上就一個天天遲到的產品在一直畫著他想做的原型圖。拿到之前版本的源碼后花了幾天看了濾鏡、貼紙的實現,濾鏡是通過CoreImage框架實現,貼紙則只是用視圖假裝的,之后面試的應聘者說到自己所掌握的圖像處理也基本上有這些內容,相信這些的實現只要花點時間就能掌握。突然有一天他提了一個需求:能不能做一個類似PS的遮罩的功能?這個問題對于當時只會數據處理展示的我真是一大阻礙。有花了一些時間,最后發現CoreAnimation框架中的CALayer就能完美實現,并且它的子類的功能十分令人驚訝,包括之后的界面效果等使用其都能很好的滿足需求,我的簡書中也有自己對CALayer的一些理解。接下來才是真正讓我遇到大困難的時候,當決定要做一款圖像處理軟件時我已經知道官方的CoreImage、CoreGraphics這些框架已經遠遠不能滿足功能需求,并且在那時候也沒特別多的時間允許我去學習諸如OpenGLES、OpenCV或者Metal等圖像處理框架,最后抱著一絲希望,從GitHub上發現了GPUImage。
? ? ? ? GPUImage的wiki并不像是一個完整的說明文檔。它只是告訴你:1.GPUImage是基于OpenGLES2.0實現;2.比CoreImage快;3.如何使用GPUImage來對一張靜態圖片、一段視頻或者攝像頭實時捕捉到的影響加一個濾鏡效果并顯示在GPUImageView上或者導出成相應格式的文件。可能只有我只看懂了以上三點,至于其他說到的例如:為什么加完濾鏡要有效果必須調用processImage方法;導出圖片前不調用useNextFrameForImageCapture方法就會crash;glsl語言有什么作用?這些問題對于當時的我完全毫無概念。并且,當時通過各搜索引擎獲取到相關的資料無論中英文都是少之又少。因此,剛試手用GPUImage去實現那些復雜的圖像處理需求時我差點選擇了放棄。這么一說,我突然好想去翻譯一下GPUImage的wiki。
? ? ? ?接下來正式說一下我對GPUImage的大體理解,如有錯誤,勞煩指正:
1.OpenGLES可以通過片段著色器(fragment)、頂點著色器(Vertex)兩方面對圖像進行做處理。GPUImage在iOS官方提供的基于OpenGLES的GLKit的再次封裝或者說封裝加拓展的庫。GLKit是可以指定使用的OpenGLES版本,而GPUImage使用的是OpenGLES2.0。個人理解,GPUImage庫中提供的大部分濾鏡是通過片段著色器的一系列操作來實現相應的效果,原因是大部分濾鏡效果并不需要改變圖片本身的大小和外形,只是對圖片中各像素進行計算生成新的像素,就可達到濾鏡或者其他調整效果,這也體現了著色器語言(glsl),尤其是片段著色器語言在GPUImage框架中的份量之重。
2.GPUImage給使用者提供了一個鏈的概念,這個鏈并不像Masonry體現在使用上,而是將每一個輸入源、濾鏡、輸出源假設為一段段獨立管道,只有將其完整串起來后,才能把輸入源的圖像信息經過每一個獨立管道進行處理最終流出得到具有所有處理效果的內容,以上同樣是個人在使用GPUImage時對其工作流程的抽象理解。
3.在2的基礎上,若想實現每個獨立管道都能串聯,GPUImage使用了一種在iOS不常用到的設計模式后:MVP。必然使用到的那些類,也就是可以加入到串聯中的那些類,1.他們都繼承于GPUImageOutput,除了GPUImageView;2.他們都遵循GPUImageInput協議,除了輸入源類(GPUImage提供了五種輸入源類:GPUImagePicture、GPUImageRawDataInput、GPUImageMovie、GPUImageUIElement、GPUImageVideoCamera,通過名字即可知道應根據輸入源類型應選擇相應類進行輸入源對象創建)。GPUImageOutput類在使用過程中并不會去直接創建該類的對象,使用者所使用到的都是它的子類。從名字可以看出,GPUImageOutput的大致作用是可以作為一個輸出源來使用,而遵循了GPUImageInput協議的類則可以理解成可以作為輸出源來使用。所以在一段段獨立管道的串聯過程中,1.最源頭的地方只要作為輸入源,即五種輸入源類不遵循GPUImageInput協議;2.在過程中需要串聯的類,比如所有filter的父類GPUImageFilter則又是GPUImageOutput的子類又要遵循GPUImageInput協議,因為它要將它上一個管道中處理好的數據接收,處理完了之后要將相應數據作為下一個的輸入源傳遞給下一個管道;3.在最后節點部分,GPUImageView就不需要繼承GPUImageOutput,原因是在它之后并不存在下一個管道。
? ? ? ? 以上是我認為GPUImage最重要的三個點,如果之后還有想起其他就再來補充。這篇是我對GPUImage理解的開卷,之后想從多方面說明對其的深入理解。想寫這些的原因其實也簡單,現在手頭的項目里依然用著GPUImage實現主要功能,但是沒想到它竟然可以強大到只要有想法就能實現各式各樣需求的地步,然后在開發過程中也希望自己能夠對它有更深入的理解,從而能幫助項目更好的優化和進步。再有嘛,可能對今后的發展也有一些幫助吧。那第一篇就到這里,以上。