什么是離屏渲染
當(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;
}
效果圖:
此時打開模擬器的
Debug->Color Off-screen Rendered
后,會發(fā)現(xiàn)一片yellow色锋恬,此時觸發(fā)了離屏渲染:原因:
首先我們呈現(xiàn)的效果圖是一個不規(guī)則的圖片屯换。這個效果是由一個規(guī)則的矩形紋理圖層+一個不規(guī)則的遮罩層組合而形成的最終效果圖,即:
這里可以看到,要想完成這樣一個效果彤悔,大致是需要分成3步的:
- 處理繪制規(guī)則紋理圖層嘉抓;
- 處理繪制不規(guī)則蒙版圖層;
- 將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ā)離屏渲染
-
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ī)則的蒙版拟淮。 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 不需要被頻繁的修改,比如處理動畫妇穴,開始光柵化反而影響效率爬虱;
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 color
和border
生效,要想讓content
也生效腾它,需要設(shè)置masksToBounds
屬性跑筝,這就回到了上一個問題。
離屏渲染使用注意
- 離屏渲染緩存內(nèi)容是有時間限制的瞒滴,緩存內(nèi)容在100ms如果沒有被使用會被丟棄曲梗,且不能復(fù)用赞警;
- 離屏渲染的緩存空間是有限的,超過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)確耕皮。等之后慢慢修正吧。