метод swift text.draw портит CGContext - PullRequest
0 голосов
/ 29 октября 2018

Я использую CoreGraphics для рисования отдельных глифов вместе с примитивами в CGContext. Следующий код работает на быстрой площадке в XCode 9.2. При запуске на площадке маленький прямоугольник с двойной буквой A должен появиться в заданных координатах на площадке liveView.

import Cocoa
import PlaygroundSupport

class MyView: NSView {

    init(inFrame: CGRect) {
        super.init(frame: inFrame)
    }

    required init?(coder decoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func draw(_ rect: CGRect) {

        // setup context properties

        let context: CGContext = NSGraphicsContext.current!.cgContext

        context.setStrokeColor(CGColor.black)
        context.setTextDrawingMode(.fill)
        context.setFillColor(CGColor(red: 0.99, green: 0.99, blue: 0.85, alpha: 1))
        context.beginPath()
        context.addRect(rect)
        context.fillPath()
        context.setFillColor(.black)

        // prepare variables and constants

        var font = CTFontCreateWithName("Helvetica" as CFString, 48, nil)
        var glyph = CTFontGetGlyphWithName(font, "A" as CFString)
        var glyph1Position = CGPoint(x: self.frame.minX, y: self.frame.maxY/2)
        var glyph2Position = CGPoint(x: self.frame.minX+150, y: self.frame.maxY/2)

        let text = "Hello"
        var textOrigin = NSPoint(x: self.frame.minX+50, y: self.frame.maxY/2)

        // draw one character

        CTFontDrawGlyphs(font, &glyph, &glyph1Position, 1, context)

        // ***   ***   when the next line is uncommented the bug appears   ***   ***

        // text.draw(at: textOrigin)

        CTFontDrawGlyphs(font, &glyph, &glyph2Position, 1, context)
    }
}

var frameRect = CGRect(x: 0, y: 0, width: 200, height: 100)

PlaygroundPage.current.liveView = MyView(inFrame: frameRect)

Теперь я хочу нарисовать обычный текст в том же контексте. Однако, когда текстовая строка рисуется между рисованием двух глифов через собственный метод рисования, текущий контекст кажется испорченным, второй глиф не будет отображаться. Когда текст рисуется после того, как нарисованы оба глифа, все в порядке.

Таким образом, очевидно, что рисование текста оказывает влияние на текущий CGContext, но я не могу выяснить, что именно происходит. Я попробовал метод saveGstate () для рисования строки и восстановления после нее, но безуспешно.

Я также пытался использовать методы CoreText для создания приписанной строки с помощью CTFramesetterCreateWithAttributedString и показывать ее с помощью CTFramesetterCreateFrame, она также не работает, здесь после создания фрейм-установщика контекст перепутался.

Моя настоящая игровая площадка более сложна, там глифы не совсем исчезают, но отображаются в неправильном вертикальном положении, но основная проблема - и вопрос тот же:

Как я могу нарисовать текст в currentContext без каких-либо других изменений в контексте, выполняемых в фоновом режиме?

1 Ответ

0 голосов
/ 29 октября 2018

Вам необходимо установить текстовую матрицу (преобразование применяется к рисованию текста). Вы должны всегда устанавливать это перед рисованием текста, потому что он не является частью графического состояния и может быть разбит другим кодом рисования, таким как строковые расширения рисования AppKit.

Добавьте следующее перед звонком CTFontDrawGlyphs:

    context.textMatrix = .identity

Это следует вызывать при первоначальной настройке контекста, поскольку нет никаких обещаний, что текстовая матрица будет идентичной перед вызовом drawRect. Затем, каждый раз, когда вы обращаетесь к чему-то, что изменяет текстовую матрицу, вам нужно будет вернуть ее обратно к тому, что вы хотите (идентичность в этом случае, но это может быть что-то другое, если вы хотите рисовать причудливым способом).

Не всегда очевидно, что изменит текстовую матрицу. Функции рисования AppKit почти всегда делают (хотя я не знаю ни одной документации, указывающей на это). Основные текстовые функции, которые изменяют контекст, такие как CTFrameDraw и CTLineDraw, обычно документируют этот факт с помощью следующего утверждения:

Этот вызов может оставить контекст в любом состоянии и не сбрасывать его после операции рисования.

Аналогично CTFontDrawGlyphs предупреждает:

Эта функция изменяет графическое состояние, включая шрифт, размер текста и текстовую матрицу, если эти атрибуты указаны в шрифте. Эти атрибуты не восстанавливаются.

Как правило, я не рекомендую смешивать системы рисования текста. Используйте AppKit или Core Text, но не смешивайте их. Если вы выберете один и придерживаетесь его, то это, как правило, не является проблемой (если вы один раз инициализируете матрицу в верхней части drawRect). Например, если вы сделали весь рисунок с CTFontDrawGlyphs, вам не нужно будет каждый раз сбрасывать матрицу; Вы бы остались в матрице Core Text, и это было бы хорошо (вот почему это работает, когда вы закомментируете вызов draw(at:)).

...