Renderowanie tekstu/czcionki w OpenGLES 2 (iOS-CoreText?)- opcje i najlepsze praktyki?

Jest wiele pytań dotyczących renderowania czcionek OpenGL, wiele z nich jest zadowolonych przez atlasy tekstowe (szybkie, ale złe) lub tekstury łańcuchowe (tylko tekst stały).

Jednak te podejścia są słabe i wydają się być przestarzałe od lat (a co z użyciem shaderów, aby zrobić to lepiej/szybciej?). Dla OpenGL 4.1 jest to doskonałe pytanie patrząc na "co należy używać Dzisiaj ?":

Co to jest state-of-the-art do renderowania tekstu w OpenGL od wersji 4.1?

Więc, co powinniśmy dziś używać na iOS GL ES 2?

Jestem rozczarowany, że wydaje się, że nie ma open-source (lub nawet rozwiązanie komercyjne). Wiem, że wiele zespołów ssie to w dół i spędza tygodnie czasu na wymyślaniu tego koła, stopniowo ucząc się jak kernować i spacji itp (ugh)- ale musi być lepszy sposób niż przepisywanie całego "fontu" od zera?


Z tego co widzę, są dwie części:

  1. Jak renderujemy tekst czcionką?
  2. Jak wyświetlić wyjście?

Dla 1 (Jak renderować), Apple zapewnia wiele sposobów na uzyskanie" poprawnego " renderowanego wyjścia - ale "łatwe" nie obsługują OpenGL (może niektóre inne tak robią - np. czy istnieje prosty sposób na mapowanie wyjścia CoreText do OpenGL?).

Dla 2 (Jak wyświetlać), mamy shadery, mamy VBOs, mamy glif-textures, mamy lookup-textures i inne tecniques (np. rzeczy związane z OpenGL 4.1 powyżej?)

Oto dwa popularne podejścia OpenGL, które znam:

  1. Atlas tekstur (renderuje wszystkie glify raz, a następnie renderuje 1 x teksturowany kwadrat na znak, ze współdzielonej tekstury)
    1. to jest złe, chyba że używasz "czcionki bitmapowej" z lat 80. (i nawet wtedy: texture atlas wymaga więcej pracy, niż może się wydawać, jeśli potrzebujesz poprawnej dla nietrywialnych czcionek) {21]}
    2. (czcionki nie są "zbiorem glifów" istnieje ogromna ilość pozycjonowania, układu, owijania, odstępy, kerning, stylizacja, koloryzacja, ważenie itp. Atlasy tekstur fail)
  2. Naprawiono ciąg znaków (użyj dowolnej klasy Apple, aby renderować poprawnie, a następnie zrzut ekranu z danych obrazu i przesłać jako teksturę)
      Po ludzku, to jest szybkie. W renderowaniu klatek jest to bardzo, bardzo wolne. Jeśli zrobisz to z dużą ilością zmieniającego się tekstu, liczba klatek na sekundę przechodzi przez podłogę
  3. technicznie jest to w większości poprawne (nie do końca: tracisz w ten sposób pewne informacje), ale ogromnie nieefektywne

Ja też widziałem, ale słyszałem i dobre i złe rzeczy o:

  1. Imagination / PowerVR " Print3D "(link uszkodzony) (od facetów, którzy produkują GPU! Ale ich strona została przeniesiona / usunięta strona renderowania tekstu)
  2. FreeType (wymaga wstępnego przetwarzania, interpretacji, dużo kodu, dodatkowych bibliotek?)
  3. ...i / lub FTGL http://sourceforge.net/projects/ftgl / (plotki: powoli? buggy? Nie aktualizowane od dłuższego czasu czas?)
  4. Font-Stash http://digestingduck.blogspot.co.uk/2009/08/font-stash.html (wysoka jakość, ale bardzo wolno?)
  5. 1.

Wewnątrz własnych bibliotek OS / standard Apple, znam kilka źródeł renderowania tekstu. NB: większość z nich wykorzystałem szczegółowo w projektach renderowania 2D, moje wypowiedzi na ich temat są oparte na bezpośrednim doświadczeniu

  1. CoreGraphics with NSString
    1. najprostszy ze wszystkich: render " do CGRect "
    2. wydaje się, że jest to nieco szybsza wersja podejścia "stałego ciągu", które ludzie zalecają (choć można oczekiwać, że będzie prawie taki sam)
  2. UILabel i UITextArea ze zwykłym tekstem
    1. NB: one nie są takie same! Niewielkie różnice w sposobie renderowania tekstu smae
  3. NSAttributedString, renderowany do jednego z powyższych
    1. ponownie: rendery inaczej (różnice, które znam, są dość subtelne i klasyfikowane jako "Błędy", różne więc pytania na ten temat)
  4. CATextLayer
    1. hybryda pomiędzy czcionkami iOS i starym renderowaniem C. Używa" Nie w pełni " płatnego pomostu cffont / UIFont, który ujawnia pewne różnice w renderowaniu / dziwności
  5. CoreText
    1. ... ostateczne rozwiązanie? Ale sama bestia...
Author: Community, 2013-09-01

3 answers

Przeprowadziłem kolejne eksperymenty i wydaje się, że CoreText może być idealnym rozwiązaniem w połączeniu z atlasem tekstur i teksturami podpisanymi przez Valve (które mogą zmienić glif bitmapowy w niezależną od rozdzielczości teksturę hi-res).

...ale jeszcze nie działa, wciąż eksperymentuje.


Aktualizacja: dokumenty Apple mówią, że dają dostęp do wszystkiego, z wyjątkiem ostatecznego szczegółu: który glif + układ glifów do renderowania (można uzyskać układ linii, a liczba glifów, ale nie sam glif, zgodnie z dokumentami). Bez wyraźnego powodu ten rdzeń informacji najwyraźniej brakuje w CoreText (jeśli tak, to czyni CT prawie bezwartościowym. Nadal poluję, aby zobaczyć, czy mogę znaleźć sposób, aby uzyskać rzeczywiste dane glpyhs + per-glyph)


UPDATE2: mam teraz to działa poprawnie z CT Apple (ale nie różne-tekstury), ale kończy się jako 3 pliki klas, 10 struktur danych, około 300 linii kodu, PLUS Kod OpenGL do renderowania go. Też dużo za odpowiedź SO: (.

Krótka odpowiedź brzmi: tak, możesz to zrobić, i to działa, Jeśli:

  1. Utwórz CTFrameSetter
  2. utwórz ramkę CTFrame dla teoretycznej ramki 2D
  3. Utwórz CGContext, który przekonwertujesz na teksturę GL
  4. W przeciwieństwie do innych aplikacji, Apple nie może korzystać z funkcji cgcontext.]} Gdy Apple renderuje glif, Oblicz boundingbox (jest to trudne) i zapisz go gdzieś]}
  5. i zapisać unikalny glif-ID (ten będzie różny dla np. "o", "f" I " of " (jeden glif!))
  6. na koniec Wyślij swój CGContext do GL jako teksturę

Podczas renderowania Użyj listy identyfikatorów glifów utworzonych przez Apple, a dla każdego z nich Użyj zapisanych informacji i tekstury, aby renderować quady za pomocą współordów tekstur, które wyciągają poszczególne glify z przesłanej tekstury.

To działa, jest szybkie, działa ze wszystkimi czcionkami, ma poprawny układ czcionek i kerning itp.

 8
Author: Adam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-14 17:15:47

Wiem, że ten post jest stary, ale natknąłem się na niego, próbując zrobić to dokładnie w mojej aplikacji. W moich poszukiwaniach natknąłem się na ten przykładowy projekt

Http://metalbyexample.com/rendering-text-in-metal-with-signed-distance-fields/

Jest to doskonała implementacja CoreText z OpenGL przy użyciu technik atlasowania tekstur i podpisanych pól odległości. To bardzo pomogło mi osiągnąć pożądane rezultaty. Mam nadzieję, że to pomoże komuś innemu.

 1
Author: aeskreis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-23 19:35:07

1.

Utwórz dowolny ciąg znaków przez NSMutableAttributedString.

let mabstring = NSMutableAttributedString(string: "This is a test of characterAttribute.")
mabstring.beginEditing()
var matrix = CGAffineTransform(rotationAngle: CGFloat(GLKMathDegreesToRadians(0)))
let font = CTFontCreateWithName("Georgia" as CFString?, 40, &matrix)
mabstring.addAttribute(kCTFontAttributeName as String, value: font, range: NSRange(location: 0, length: 4))
var number: Int8 = 2
let kdl = CFNumberCreate(kCFAllocatorDefault, .sInt8Type, &number)!
mabstring.addAttribute(kCTStrokeWidthAttributeName as String, value: kdl, range: NSRange(location: 0, length: mabstring.length))
mabstring.endEditing()

2.

Utwórz CTFrame. rect Oblicz z mabstring za pomocą CoreText.CTFramesetterSuggestFrameSizeWithConstraints

let framesetter = CTFramesetterCreateWithAttributedString(mabstring)
let path = CGMutablePath()
path.addRect(rect)
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)

3.

Tworzenie kontekstu bitmapowego.

let imageWidth = Int(rect.width)
let imageHeight = Int(rect.height)
var rawData = [UInt8](repeating: 0, count: Int(imageWidth * imageHeight * 4))
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitsPerComponent = 8
let bytesPerRow = Int(rect.width) * 4
let context = CGContext(data: &rawData, width: imageWidth, height: imageHeight, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: rgbColorSpace, bitmapInfo: bitmapInfo.rawValue)!

4.

Rysowanie CTFrame w kontekście bitmapowym.

CTFrameDraw(frame, context)

Teraz mamy surowe dane pikseli rawData. Utwórz OpenGL Texture , MTLTexture , UIImage z rawData jest ok.


Przykład,

Do Tekstury OpenGL:Konwersja interfejsu użytkownika w Tekstura

Ustaw teksturę:

GLuint textureID;    
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &textureID);

glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);

,

//to MTLTexture
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm, width: Int(imageWidth), height: Int(imageHeight), mipmapped: true)
let device = MTLCreateSystemDefaultDevice()!
let texture = device.makeTexture(descriptor: textureDescriptor)
let region = MTLRegionMake2D(0, 0, Int(imageWidth), Int(imageHeight))
texture.replace(region: region, mipmapLevel: 0, withBytes: &rawData, bytesPerRow: imageRef.bytesPerRow)

,

//to UIImage
  let providerRef = CGDataProvider(data: NSData(bytes: &rawData, length: rawData.count * MemoryLayout.size(ofValue: UInt8(0))))
  let renderingIntent =  CGColorRenderingIntent.defaultIntent
  let imageRef = CGImage(width: imageWidth, height: imageHeight, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: bytesPerRow, space: rgbColorSpace, bitmapInfo: bitmapInfo, provider: providerRef!, decode: nil, shouldInterpolate: false, intent: renderingIntent)!
  let image = UIImage.init(cgImage: imageRef)
 0
Author: jimmy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:16:46