# 拉伸過濾
????最后我們再來談談`minificationFilter`和`magnificationFilter`屬性。總得來講,當我們視圖顯示一個圖片的時候,都應該正確地顯示這個圖片(意即:以正確的比例和正確的1:1像素顯示在屏幕上)。原因如下:
* 能夠顯示最好的畫質,像素既沒有被壓縮也沒有被拉伸。
* 能更好的使用內存,因為這就是所有你要存儲的東西。
* 最好的性能表現,CPU不需要為此額外的計算。
????不過有時候,顯示一個非真實大小的圖片確實是我們需要的效果。比如說一個頭像或是圖片的縮略圖,再比如說一個可以被拖拽和伸縮的大圖。這些情況下,為同一圖片的不同大小存儲不同的圖片顯得又不切實際。
????當圖片需要顯示不同的大小的時候,有一種叫做*拉伸過濾*的算法就起到作用了。它作用于原圖的像素上并根據需要生成新的像素顯示在屏幕上。
????事實上,重繪圖片大小也沒有一個統一的通用算法。這取決于需要拉伸的內容,放大或是縮小的需求等這些因素。`CALayer`為此提供了三種拉伸過濾方法,他們是:
* kCAFilterLinear
* kCAFilterNearest
* kCAFilterTrilinear
????minification(縮小圖片)和magnification(放大圖片)默認的過濾器都是`kCAFilterLinear`,這個過濾器采用雙線性濾波算法,它在大多數情況下都表現良好。雙線性濾波算法通過對多個像素取樣最終生成新的值,得到一個平滑的表現不錯的拉伸。但是當放大倍數比較大的時候圖片就模糊不清了。
????`kCAFilterTrilinear`和`kCAFilterLinear`非常相似,大部分情況下二者都看不出來有什么差別。但是,較雙線性濾波算法而言,三線性濾波算法存儲了多個大小情況下的圖片(也叫多重貼圖),并三維取樣,同時結合大圖和小圖的存儲進而得到最后的結果。
????這個方法的好處在于算法能夠從一系列已經接近于最終大小的圖片中得到想要的結果,也就是說不要對很多像素同步取樣。這不僅提高了性能,也避免了小概率因舍入錯誤引起的取樣失靈的問題

圖4.14 對于大圖來說,雙線性濾波和三線性濾波表現得更出色
????`kCAFilterNearest`是一種比較武斷的方法。從名字不難看出,這個算法(也叫最近過濾)就是取樣最近的單像素點而不管其他的顏色。這樣做非常快,也不會使圖片模糊。但是,最明顯的效果就是,會使得壓縮圖片更糟,圖片放大之后也顯得塊狀或是馬賽克嚴重。

圖4.15 對于沒有斜線的小圖來說,最近過濾算法要好很多
????總的來說,對于比較小的圖或者是差異特別明顯,極少斜線的大圖,最近過濾算法會保留這種差異明顯的特質以呈現更好的結果。但是對于大多數的圖尤其是有很多斜線或是曲線輪廓的圖片來說,最近過濾算法會導致更差的結果。換句話說,線性過濾保留了形狀,最近過濾則保留了像素的差異。
????讓我們來實驗一下。我們對第三章的時鐘項目改動一下,用LCD風格的數字方式顯示。我們用簡單的像素字體(一種用像素構成字符的字體,而非矢量圖形)創造數字顯示方式,用圖片存儲起來,而且用第二章介紹過的拼合技術來顯示(如圖4.16)。

圖4.16 一個簡單的運用拼合技術顯示的LCD數字風格的像素字體
????我們在Interface Builder中放置了六個視圖,小時、分鐘、秒鐘各兩個,圖4.17顯示了這六個視圖是如何在Interface Builder中放置的。如果每個都用一個淡出的outlets對象就會顯得太多了,所以我們就用了一個`IBOutletCollection`對象把他們和控制器聯系起來,這樣我們就可以以數組的方式訪問視圖了。清單4.6是代碼實現。
清單4.6 顯示一個LCD風格的時鐘
~~~
@interface ViewController ()
@property (nonatomic, strong) IBOutletCollection(UIView) NSArray *digitViews;
@property (nonatomic, weak) NSTimer *timer;
??
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad]; //get spritesheet image
UIImage *digits = [UIImage imageNamed:@"Digits.png"];
//set up digit views
for (UIView *view in self.digitViews) {
//set contents
view.layer.contents = (__bridge id)digits.CGImage;
view.layer.contentsRect = CGRectMake(0, 0, 0.1, 1.0);
view.layer.contentsGravity = kCAGravityResizeAspect;
}
//start timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick) userInfo:nil repeats:YES];
//set initial clock time
[self tick];
}
- (void)setDigit:(NSInteger)digit forView:(UIView *)view
{
//adjust contentsRect to select correct digit
view.layer.contentsRect = CGRectMake(digit * 0.1, 0, 0.1, 1.0);
}
- (void)tick
{
//convert time to hours, minutes and seconds
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar];
NSUInteger units = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
?
NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];
//set hours
[self setDigit:components.hour / 10 forView:self.digitViews[0]];
[self setDigit:components.hour % 10 forView:self.digitViews[1]];
//set minutes
[self setDigit:components.minute / 10 forView:self.digitViews[2]];
[self setDigit:components.minute % 10 forView:self.digitViews[3]];
//set seconds
[self setDigit:components.second / 10 forView:self.digitViews[4]];
[self setDigit:components.second % 10 forView:self.digitViews[5]];
}
@end
~~~
如圖4.18,這樣做的確起了效果,但是圖片看起來模糊了。看起來默認的`kCAFilterLinear`選項讓我們失望了。

圖4.18 一個模糊的時鐘,由默認的`kCAFilterLinear`引起
????為了能像圖4.19中那樣,我們需要在for循環中加入如下代碼:
~~~
view.layer.magnificationFilter = kCAFilterNearest;
~~~

圖4.19 設置了最近過濾之后的清晰顯示
- Introduction
- 1. 圖層樹
- 1.1 圖層與視圖
- 1.2 圖層的能力
- 1.3 使用圖層
- 1.4 總結
- 2. 寄宿圖
- 2.1 contents屬性
- 2.2 Custom Drawing
- 2.3 總結
- 3. 圖層幾何學
- 3.1 布局
- 3.2 錨點
- 3.3 坐標系
- 3.4 Hit Testing
- 3.5 自動布局
- 3.6 總結
- 4. 視覺效果
- 4.1 圓角
- 4.2 圖層邊框
- 4.3 陰影
- 4.4 圖層蒙板
- 4.5 拉伸過濾
- 4.6 組透明
- 4.7 總結
- 5. 變換
- 5.1 仿射變換
- 5.2 3D變換
- 5.3 固體對象
- 5.4 總結
- 6. 專用圖層
- 6.1 CAShapeLayer
- 6.2 CATextLayer
- 6.3 CATransformLayer
- 6.4 CAGradientLayer
- 6.5 CAReplicatorLayer
- 6.6 CAScrollLayer
- 6.7 CATiledLayer
- 6.8 CAEmitterLayer
- 6.9 CAEAGLLayer
- 6.10 AVPlayerLayer
- 6.11 總結
- 7. 隱式動畫
- 7.1 事務
- 7.2 完成塊
- 7.3 圖層行為
- 7.4 呈現與模型
- 7.5 總結
- 8. 顯式動畫
- 8.1 屬性動畫
- 8.2 動畫組
- 8.3 過渡
- 8.4 在動畫過程中取消動畫
- 8.5 總結
- 9. 圖層時間
- 9.1 CAMediaTiming協議
- 9.2 層級關系時間
- 9.3 手動動畫
- 9.4 總結
- 10. 緩沖
- 10.1 動畫速度
- 10.2 自定義緩沖函數
- 10.3 總結
- 11. 基于定時器的動畫
- 11.1 定時幀
- 11.2 物理模擬
- 12. 性能調優
- 12.1. CPU VS GPU
- 12.2 測量,而不是猜測
- 12.3 Instruments
- 12.4 總結
- 13. 高效繪圖
- 13.1 軟件繪圖
- 13.2 矢量圖形
- 13.3 臟矩形
- 13.4 異步繪制
- 13.5 總結
- 14. 圖像IO
- 14.1 加載和潛伏
- 14.2 緩存
- 14.3 文件格式
- 14.4 總結
- 15. 圖層性能
- 15.1 隱式繪制
- 15.2 離屏渲染
- 15.3 混合和過度繪制
- 15.4 減少圖層數量
- 15.5 總結