guide framework development developing developer desarrollador app apis ios opengl-es fonts core-animation core-text

framework - Representación de texto/fuente en OpenGLES 2(iOS-¿CoreText?): ¿Opciones y mejores prácticas?



ios framework (3)

Hay muchas preguntas sobre la representación de fuentes OpenGL, muchas de ellas se satisfacen con atlas de textura (rápido, pero incorrecto), o texturas de cadena (solo texto fijo).

Sin embargo, esos enfoques son deficientes y parecen estar desactualizados por años (¿qué hay de usar los shaders para hacerlo mejor / más rápido?). Para OpenGL 4.1, hay una excelente pregunta sobre "¿qué debería usar hoy ?":

¿Qué es el estado del arte para la representación de texto en OpenGL a partir de la versión 4.1?

Entonces, ¿qué deberíamos estar usando en iOS GL ES 2 hoy?

Estoy decepcionado de que no parece haber una fuente abierta (ni siquiera una solución comercial). Sé que muchos equipos lo absorben y pasan semanas de tiempo de desarrollo reinventando esta rueda, aprendiendo poco a poco a kern y al espacio, etc. (ugh), pero debe haber una mejor manera que volver a escribir la totalidad de las "fuentes". desde cero?

Por lo que puedo ver, hay dos partes a esto:

  1. ¿Cómo renderizamos texto usando una fuente?
  2. ¿Cómo mostramos la salida?

Para 1 (cómo generar), Apple proporciona MUCHAS maneras de obtener la salida representada "correcta", pero las "fáciles" no son compatibles con OpenGL (tal vez algunas de las otras lo hacen, por ejemplo, ¿existe una forma sencilla de asignar la salida CoreText? a OpenGL?).

Para 2 (cómo mostrar), tenemos sombreadores, tenemos VBO, tenemos texturas con glifos, tenemos texturas de búsqueda y otras técnicas (por ejemplo, las cosas de OpenGL 4.1 vinculadas anteriormente).

Estos son los dos enfoques comunes de OpenGL que conozco:

  1. Atlas de textura (renderiza todos los glifos una vez, luego renderiza 1 x cuádruple texturado por personaje, de la textura compartida)
    1. Esto es incorrecto, a menos que esté usando una "fuente de mapa de bits" de la era de la década de 1980 (e incluso entonces: el atlas de textura requiere más trabajo del que puede parecer, si lo necesita para fuentes no triviales)
    2. (las fuentes no son "una colección de glifos" hay una gran cantidad de posicionamiento, diseño, envoltura, espaciado, kerning, estilo, coloreado, ponderación, etc. Los atlas de textura fallan)
  2. Cadena fija (use cualquier clase de Apple para renderizar correctamente, luego haga una captura de pantalla de los datos de imagen de respaldo y cárguelos como una textura)
    1. En términos humanos, esto es rápido. En la renderización de cuadros, esto es muy, muy lento. Si haces esto con un montón de texto cambiante, tu velocidad de fotogramas pasa por el piso
    2. Técnicamente, es en su mayoría correcto (no del todo: se pierde información de esta manera) pero es enormemente ineficiente

También he visto, pero he oído cosas buenas y malas sobre:

  1. Imagination / PowerVR "Print3D" (enlace roto) (de los tipos que fabrican la GPU. Pero su sitio ha movido / eliminado la página de representación de texto)
  2. FreeType (requiere preprocesamiento, interpretación, muchos códigos, bibliotecas adicionales)
  3. ... y / o FTGL http://sourceforge.net/projects/ftgl/ (rumores: ¿lento? ¿Buggy? ¿No se ha actualizado en mucho tiempo?)
  4. Font-Stash http://digestingduck.blogspot.co.uk/2009/08/font-stash.html (alta calidad, ¿pero muy lenta?)
  5. 1.

Dentro del propio sistema operativo / bibliotecas estándar de Apple, conozco varias fuentes de procesamiento de texto. NB: He usado la mayoría de estos en detalle en proyectos de renderización en 2D, mis declaraciones sobre la salida de diferentes renderizaciones se basan en la experiencia directa

  1. CoreGraphics con NSString
    1. Lo más simple de todo: render "en un CGRect"
    2. Parece ser una versión ligeramente más rápida del enfoque de "cadena fija" que recomiendan las personas (aunque usted esperaría que fuera muy similar)
  2. UILabel y UITextArea con texto plano.
    1. NB: ¡NO son lo mismo! Pequeñas diferencias en la forma en que renderizan el texto smae.
  3. NSAttributedString, rendido a uno de los anteriores
    1. Nuevamente: se presenta de manera diferente (las diferencias que conozco son bastante sutiles y están clasificadas como "errores", varias preguntas de SO sobre esto)
  4. CATextLayer
    1. Un híbrido entre las fuentes de iOS y el viejo renderizado de C Utiliza el CFFont / UIFont con puente gratuito "no totalmente", que revela algunas diferencias / extrañeza de renderización más
  5. CoreText
    1. ... la solución definitiva? Pero una bestia propia ...

1.

Crear cualquier cadena por 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.

Crear CTFrame. El rect calcula de mabstring por CoreText. CTFramesetterSuggestFrameSizeWithConstraints

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

3.

Crear contexto de mapa de bits.

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.

Dibuja CTFrame en el contexto de mapa de bits.

CTFrameDraw(frame, context)

Ahora, tenemos los datos crudos de datos en rawData . Crear OpenGL Texture , MTLTexture , UIImage con rawData está bien.

Ejemplo,

Para OpenGL Texture: Convertir un UIImage en una textura

Configura tu textura:

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)


Hice algunos experimentos más, y parece que CoreText podría ser una solución perfecta cuando se combina con un atlas de texturas y texturas con diferencias firmadas de Valve (que pueden convertir un glifo de mapa de bits en una textura de alta resolución independiente de la resolución).

... pero todavía no lo tengo funcionando, todavía estoy experimentando.

ACTUALIZACIÓN: los documentos de Apple dicen que le dan acceso a todo excepto el detalle final: qué diseño de glifo + glifo se debe representar (puede obtener el diseño de línea y el número de glifos, pero no el glifo en sí, según los documentos). Sin ninguna razón aparente, esta información central aparentemente falta en CoreText (si es así, eso hace que la TC sea casi inútil. Todavía estoy buscando para ver si puedo encontrar una manera de obtener los datos reales de glpyhs + por glifo)

ACTUALIZACIÓN2: Ahora tengo esto funcionando correctamente con el CT de Apple (pero no con texturas diferentes), pero termina como archivos de 3 clases, 10 estructuras de datos, aproximadamente 300 líneas de código, más el código OpenGL para representarlo. Demasiado para una respuesta SO :(.

La respuesta corta es: sí, puedes hacerlo, y funciona, si:

  1. Crear CTFrameSetter
  2. Crear CTFrame para un marco teórico 2D
  3. Crea un CGContext que convertirás en una textura GL
  4. Ir a través de glifo por glifo, permitiendo que Apple se renderice al CGContext
  5. Cada vez que Apple muestre un glifo, calcule el cuadro delimitador (esto es DIFÍCIL) y guárdelo en algún lugar
  6. Y guarde el ID de glifo único (esto será diferente para, por ejemplo, "o", "f" y "de" (¡un glifo!))
  7. Finalmente, envía tu CGContext hasta GL como una textura

Cuando renderice, use la lista de identificadores de glifos que Apple creó, y para cada uno use la información guardada, y la textura, para renderizar quads con co-ords de texturas que eliminan los glifos individuales de la textura que cargó.

Esto funciona, es rápido, funciona con todas las fuentes, hace que todo el diseño de la fuente y el kerning sean correctos, etc.


Sé que esta publicación es antigua, pero la encontré al intentar hacer esto exactamente en mi aplicación. En mi búsqueda, me encontré con este proyecto de muestra

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

Es una implementación perfecta de CoreText con OpenGL utilizando las técnicas de atlas de textura y campos de distancia firmados. Me ha ayudado mucho a lograr los resultados que quería. Espero que esto ayude a alguien más.