IOS繪製1畫素線條問題
前段時間美術在驗收介面時提了問題:為啥要求1畫素寬的一個矩形框似乎卻變成了2,3個畫素寬。仔細檢查過程式碼後發現,的確設定了LineWidth為1,但繪製效果卻並不如人願。似乎在ios上繪製最低要2個畫素的線寬。
檢視文件後發現造成這個問題的原因是Quartz的抗鋸齒機制。一種粗暴的解決方案是不採用抗鋸齒,即:CGContextSetShouldAntialias(context, NO)。但是顯而易見的問題是取消抗鋸齒會導致繪製效果變差。而另外一種方案則比較取巧:將繪製調整到半畫素座標系上:
比如 CGContextMoveToPoint(context, 100.0, 100.0
這是因為:所謂的線寬指的是給定路徑的中心到兩邊的粗細,換句話是在路徑的兩邊各繪製一半。如圖
在繪製線寬為1的直線(3,1)到(3,5)時,實際上是佔據了左右兩個畫素各半個畫素,而真正繪製時當然是以一個畫素為標準單位,所以淺藍色區域就會以相近的方式進行渲染。這也是寬為1.0的線繪製並不準確的原因。而當將繪製中心調整到半個畫素上就不會有這個問題,見右圖:(3.5,1)到(3.5,5)。詳細可以參考
最後上一個在ios上繪製帶圓角矩形的程式碼:
if (radius <= 0) { CGContextAddRect(context, rect); return; } CGContextSaveGState(context); CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); CGContextScaleCTM(context, radius, radius); NSInteger width = CGRectGetWidth(rect) / radius; NSInteger height = CGRectGetHeight(rect) / radius; NSInteger halfWidth = width / 2; NSInteger halfHeight= height / 2; CGContextMoveToPoint (context, width + 0.5, halfHeight + 0.5); CGContextAddArcToPoint(context, width + 0.5, height + 0.5, halfWidth + 0.5, height + 0.5, 1); CGContextAddArcToPoint(context, 0 + 0.5, height + 0.5, 0 + 0.5, halfHeight + 0.5, 1); CGContextAddArcToPoint(context, 0 + 0.5, 0 + 0.5, halfWidth + 0.5, 0 + 0.5, 1); CGContextAddArcToPoint(context, width + 0.5, 0 + 0.5, width + 0.5, halfHeight + 0.5, 1); CGContextClosePath(context); CGContextRestoreGState(context);
以下是自己測試得出的結論,如有異意請各位老師不吝賜教,謝謝!
由於retina螢幕是640畫素,而非retina是320,所以,分兩種情況,在retina螢幕下和非retina螢幕下。
畫一條從點(10.0, 10.0)到點(10.0, 100.0)的直線和點(10.5, 10.5)到點(10.5, 100.5)的直線,產生的樣式,上面都解釋了。
我想說的是從點(10.3, 10.3)到點(10.3, 100.3)的黑色一個畫素的直線,CGContextSetShouldAntialias(context, YES)。
一、在retina螢幕下
====這個圖片是我畫出的線放大後的效果,我計算了,它繪製的是20、21、22這三個畫素值,也就是說10.3 * 2 = 20.6;那麼它會將第21個畫素值繪製黑色,兩邊的繪製暗色。
二、在非retina螢幕下
====這個圖片是我畫出的線放大後的效果,計算後,它繪製的是10、11這兩個畫素值,那麼它會將第11個畫素值繪製黑色,10的繪製成暗色。
我還要說從點(10.7, 10.7)到點(10.7, 100.7)的黑色一個畫素的直線,CGContextSetShouldAntialias(context, YES)。
一、在retina螢幕下
圖片樣式還是一樣的,但是位置變了,這次畫的是21、22、23這三個畫素值,也就是說10.7 * 2 = 21.4;那麼他是將第22個畫素值繪製黑色,兩邊的繪製暗色。
二、在非retina螢幕下
====這個圖片是我畫出的線放大後的效果,計算後,它繪製的是11、12這兩個畫素值,那麼它會將第11個畫素值繪製黑色,12的繪製成暗色。
在說一說CGContextSetShouldAntialias(context, NO)。10.3和10.7的結果如下
一、在retina螢幕下
繪製位置都一樣,但是樣式變了,變成沒有暗色全是黑色。
二、在非retina螢幕下
位置樣式都變了,顏色只是黑色,並且都只畫了第11個畫素
總結-----
其實就一句話,“所謂的線寬指的是給定路徑的中心到兩邊的粗細,換句話是在路徑的兩邊各繪製一半”。這個原理明白了,啥都沒問題了。