iOS-對離屏渲染的理解

什么是離屏渲染

當(dāng)圖層屬性的混合體被指定為在未預(yù)合成之前不能直接在屏幕中繪制時笛质,屏幕外渲染就被喚起了泉沾。屏幕外渲染并不意味著軟件繪制,但是它意味著圖層必須在被顯示之前在一個屏幕外上下文中被渲染(不論CPU還是GPU)妇押。---摘自iOS核心動畫

為什么會觸發(fā)離屏渲染

觸發(fā)條件:

  • 圓角(當(dāng)和maskToBounds一起使用時)
  • 圖層蒙板
  • 陰影

簡單來講,當(dāng)一個視圖無法通過一次繪制并完成渲染時跷究,就會觸發(fā)離屏渲染。具體來講就是舆吮,當(dāng)一個視圖效果需要多個圖層配合完成時揭朝,此時會開啟離屏緩存區(qū)(Off-Screen Buffer),來處理并緩存每一個圖層的渲染結(jié)果,最終組合提交到幀緩存區(qū)(Frame Buffer)色冀,并完成最終的渲染效果潭袱。
舉個例子:給UIImageView 設(shè)置 mask:

@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad
{
  [super viewDidLoad];
  //create mask layer
  self.imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 350, 240)];
    self.imageView.center = self.view.center;
    self.imageView.image = [UIImage imageNamed:@"linjj"];
    self.imageView.backgroundColor = [UIColor redColor];
    [self.view addSubview:self.imageView];
//    self.imageView.layer.cornerRadius = 15.0;
//    self.imageView.layer.masksToBounds = YES;

    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.path = [self getCustomPath].CGPath;
    self.imageView.layer.mask = maskLayer;
}

效果圖:

自定義mask效果圖.png

此時打開模擬器的Debug->Color Off-screen Rendered后,會發(fā)現(xiàn)一片yellow色锋恬,此時觸發(fā)了離屏渲染:
觸發(fā)了離屏渲染.png

原因:
首先我們呈現(xiàn)的效果圖是一個不規(guī)則的圖片屯换。這個效果是由一個規(guī)則的矩形紋理圖層+一個不規(guī)則的遮罩層組合而形成的最終效果圖,即:

流程圖.png
蒙版.png
JJ紋理.png
組合.png

這里可以看到,要想完成這樣一個效果彤悔,大致是需要分成3步的:

  1. 處理繪制規(guī)則紋理圖層嘉抓;
  2. 處理繪制不規(guī)則蒙版圖層;
  3. 將1和2組合并提交給FrameBuffer晕窑,渲染結(jié)果抑片。

既然需要分別處理和組合,那么1和2的狀態(tài)就需要保存杨赤,此時就自動觸發(fā)了Off-screen Buffer,來保存每一步的狀態(tài)敞斋,最終將保存的狀態(tài)提交到FrameBuffer
關(guān)于FrameBuffer疾牲,這個里面一定放的是即將要呈現(xiàn)的畫面或者是已經(jīng)處理好的畫面(幀)植捎。

觸發(fā)離屏渲染

  1. self.imageView.layer.masksToBounds = YES
    這個設(shè)置為YES,會觸發(fā)離屏渲染阳柔,這里完全也可以理解為給一個layer設(shè)置了mask,只不過是這是個特殊的焰枢,規(guī)則的,系統(tǒng)內(nèi)部自動添加的mask舌剂,同樣需要多個步驟+組合济锄,最終到屏幕。通常跟self.imageView.layer.cornerRadius = 15.0一起使用架诞,來設(shè)置一個規(guī)則的蒙版拟淮。
  2. self.imageView.layer.shouldRasterize = YES

When true, the layer is rendered as a bitmap in its local coordinate
space ("rasterized"), then the bitmap is composited into the
destination (with the minificationFilter and magnificationFilter
properties of the layer applied if the bitmap needs scaling).
Rasterization occurs after the layer's filters and shadow effects
are applied, but before the opacity modulation. As an implementation
detail the rendering engine may attempt to cache and reuse the
bitmap from one frame to the next. (Whether it does or not will have
no affect on the rendered output.)

注意關(guān)鍵詞:composited into the destination(合成到目標(biāo)),既然要合成谴忧,就一定是多個步驟完成很泊,就需要開啟Off-screen Buffer 保存一些繪制狀態(tài),最后提交到FrameBuffer渲染沾谓。
關(guān)于shouldRasterize的使用建議:

  • layer 是可復(fù)用的委造,設(shè)置為YES,是可以提高效率的均驶,反正不使用昏兆;
  • layer 不需要被頻繁的修改,比如處理動畫妇穴,開始光柵化反而影響效率爬虱;
  1. self.imageView.layer.cornerRadius = 15.0

Setting the radius to a value greater than 0.0 causes the layer to begin drawing rounded corners on its background. By default, the corner radius does not apply to the image in the layer’s contents property; it applies only to the background color and border of the layer. However, setting the masksToBounds property to true causes the content to be clipped to the rounded corners.

只設(shè)置cornerRadius只會對background colorborder生效,要想讓content也生效腾它,需要設(shè)置masksToBounds屬性跑筝,這就回到了上一個問題。

離屏渲染使用注意

  1. 離屏渲染緩存內(nèi)容是有時間限制的瞒滴,緩存內(nèi)容在100ms如果沒有被使用會被丟棄曲梗,且不能復(fù)用赞警;
  2. 離屏渲染的緩存空間是有限的,超過2.5倍的屏幕像素大小虏两,會失效愧旦。

避免離屏渲染

使用CAShaperLayer+UIBezierPath畫圓角:

- (void)viewDidLoad
{
    [super viewDidLoad];
    //create shape layer
    CAShapeLayer *blueLayer = [CAShapeLayer layer];
    blueLayer.frame = CGRectMake(50, 50, 100, 100);
    blueLayer.fillColor = [UIColor blueColor].CGColor;
    blueLayer.path = [UIBezierPath bezierPathWithRoundedRect:
    CGRectMake(0, 0, 100, 100) cornerRadius:20].CGPath;
    //add it to our view
    [self.layerView.layer addSublayer:blueLayer];
}

對UIImage 切圓角:

- (UIImage *)roundedCornerImageWithCornerRadius:(CGFloat)cornerRadius
{
    CGFloat w = self.size.width;
    CGFloat h = self.size.height;
    CGFloat scale = [UIScreen mainScreen].scale;
    if(cornerRadius < 0){
        cornerRadius = 0;
    }else if (cornerRadius > MIN(w, h)){
        cornerRadius = MIN(w, h) / 2;
    }
    UIImage *image = nil;
    CGRect imageFrame = CGRectMake(0, 0, w, h);
    UIGraphicsBeginImageContextWithOptions(self.size, NO, scale);
    [UIBezierPath bezierPathWithRoundedRect:imageFrame cornerRadius:cornerRadius];
    [self drawInRect:imageFrame];
    image = UIGraphicsGetImageFromCurrentImageContext();
    return image;
}

方案有很多,這里不一一贅述了定罢。笤虫。

總結(jié)

以上內(nèi)容,都是自己的一些理解祖凫,可能不是很準(zhǔn)確耕皮。等之后慢慢修正吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝙场,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子粱年,更是在濱河造成了極大的恐慌售滤,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件台诗,死亡現(xiàn)場離奇詭異完箩,居然都是意外死亡,警方通過查閱死者的電腦和手機拉队,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門弊知,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粱快,你說我怎么就攤上這事秩彤。” “怎么了事哭?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵漫雷,是天一觀的道長。 經(jīng)常有香客問我鳍咱,道長降盹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任谤辜,我火速辦了婚禮蓄坏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丑念。我一直安慰自己涡戳,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布渠欺。 她就那樣靜靜地躺著妹蔽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胳岂,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天编整,我揣著相機與錄音,去河邊找鬼乳丰。 笑死掌测,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的产园。 我是一名探鬼主播汞斧,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼什燕!你這毒婦竟也來了粘勒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤屎即,失蹤者是張志新(化名)和其女友劉穎庙睡,沒想到半個月后技俐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乘陪,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡雕擂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了井赌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谤逼。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖森缠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贵涵,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布恰画,位于F島的核電站宾茂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拴还。R本人自食惡果不足惜跨晴,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一片林、第九天 我趴在偏房一處隱蔽的房頂上張望怀骤。 院中可真熱鬧,春花似錦蒋伦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乙帮。三九已至演怎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間探橱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留迈窟,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓车酣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親湖员。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348