2017-12-02 269 views
2

:我代替我的原代碼存根與自足版本CTFontDrawGlyphs繪製編輯時間不正確的位置

我使用Quartz2D畫音樂符號的CGContext上。我正在使用Swift 4在Playground中做這件事。

音符由用於音符的單個CGGlyph和用於莖的線原語組成。字形取自音樂字體「bravura」。 書寫筆字形的x位置正好位於其左邊緣。莖必須完全對齊這個原點,因此它的X位置必須是紙頭的X位置+莖的厚度/ 2.

我有一個函數,它將CGFloat作爲x位置並繪製字形和幹到CGContext。當x的值是像30.0這樣的四捨五入值時,這可以很好地工作。然而,當值就像是30.9的分數,莖被錯位:

picture of 1 good and 1 bad note

很明顯,該線正確繪製,但字形忽略CGFloat的小數部分。當我將x值進一步提高到31.0時,兩個部分再次同步。

這裏是簡化代碼,這會在你的XCode9遊樂場工作:

import PlaygroundSupport 
import Cocoa 

let fontSize = CGFloat(64) 

// uncomment the next paragraph if you have added the "Bravura" Font to the ressources folder 
/* 
let fontURL = Bundle.main.url(forResource: "Bravura", withExtension: "otf") 
CTFontManagerRegisterFontsForURL(fontURL! as CFURL, CTFontManagerScope.process, nil) 
var font = CTFontCreateWithName("Bravura" as CFString, fontSize, nil) 
*/ 

// comment out the next line if you use Bravura 
var font = CTFontCreateWithName("Helvetica" as CFString, fontSize, nil) 

func drawAtXPosition(_ posX: CGFloat, inContext context: CGContext) { 
    // draw font glyph (notehead) 
    var glyph = CTFontGetGlyphWithName(font, "uniE0A3" as CFString) 
    var notePosition = CGPoint(x: posX, y: 100) 
    CTFontDrawGlyphs(font, &glyph, &notePosition, 1, context) 

    // draw line (note stem) 
    let stemThickness = CGFloat(fontSize/4*0.12) 
    let x: CGFloat = posX + stemThickness/2 
    let y: CGFloat = 100 - (0.168 * fontSize/4) 
    let stemBegin = CGPoint(x:x, y:y) 
    let stemEnd = CGPoint(x:x, y: y - (3.5 * fontSize/4)) 

    context.setLineWidth(stemThickness) 
    context.strokeLineSegments(between: [stemBegin, stemEnd]) 
} 

class MyView: NSView { 
    override func draw(_ rect: CGRect) { 
     let context = NSGraphicsContext.current!.cgContext 
     context.setStrokeColor(CGColor.black) 
     context.setFillColor(CGColor.black) 

     drawAtXPosition(100, inContext: context) // this draws as expected 

     drawAtXPosition(200.99, inContext: context) // here the glyph is drawn at x=200, while the line is correctly drawn at 200.99 
    } 
} 

var myView = MyView(frame: CGRect(x: 0, y: 0, width: 500, height: 200)) 
let myViewController = NSViewController() 
myViewController.view = myView 
PlaygroundPage.current.liveView = myViewController 

您可以從下載音樂字體 「炫耀的表現」 ......

https://www.smufl.org/fonts/

...但我重寫了代碼,因此它默認使用Helvetica,這也適用於演示。

作爲我原始代碼中的解決方法,我在將x值傳遞給繪圖函數之前實現了Foundation的.rounded()方法。這是有效的,但恐怕在小字號(整個代碼的寫入使得所有的字體都是圍繞字體大小)時,這種不準確將會引起人們的注意。

我遇到了CGContext的setShouldSubpixelPositionFonts和setAllowsFontSubpixelPositioning方法,但將它們設置爲true並沒有做任何改變。

那麼,有沒有一種方法可以像原始圖一樣精確繪製字體對象(必須有一個)?或者,我擔心將CGFloats四捨五入到Ints是不合理的?

+0

添加完整的自包含代碼可能會有幫助,其他人可以將其複製到Playground中以重現問題並嘗試可能的解決方案。 –

+0

謝謝指出。隨着我的第一個版本,我希望能夠找到遇到同樣問題的人,所以我很快包裝了一些線來證明問題。正如你所看到的,一個工作版本實際上需要更多的代碼,但現在我終於有時間重寫我的第一個版本了。 – MassMover

回答

0

無盡的挖掘後,我找到了解決辦法:

這是不夠的設定

context.setShouldSubpixelPositionFonts(true) 

您還可以設置

context.setShouldSubpixelQuantizeFonts(false) 

,因爲後者似乎超越了前者。