2010-11-27 17 views
9

Tengo un texto variable en un NSTextField que se representa en una vista de fondo de CALayer. Como CALayer no es compatible con el alias de subpíxeles para la representación de texto de cualquier texto en la parte superior, este texto se ve como basura.Cómo evitar la representación de texto deficiente en texto respaldado por un CALayer

Un poco de Google revela las razones por las que esto es así, y ese texto se debe representar sobre un fondo opaco para tener habilitado SPA. Representar sobre un fondo opaco es algo que me gustaría evitar si es posible en este caso. ¿Hay una mejor solución?

Estoy completamente dispuesto a renderizar el texto yo mismo en un NSImage, si eso ayuda, pero no puedo encontrar ningún informe confirmado que lo haga.

Se ve absolutamente bien en Interface Builder, así que sé que el secreto está en algún lugar dentro de esta computadora que se esfuerza por salir.

+1

¿Podría publicar el código utilizado para modificar la capa de NSTextView? –

+0

Hay muy poco código real porque principalmente hago cosas en Interface Builder. Hay un NSView que controla un NSImageVie –

+0

... que controla un NSImageView, con un PNG semitransparente. El texto se encuentra encima de eso. –

Respuesta

3

Solución encontrado. Nada en Quartz puede mostrar texto con Alias ​​de subpíxeles sobre un fondo transparente, al parecer. Sin embargo, PUEDE procesar texto en un búfer de mapa de bits fuera de pantalla, siempre que el búfer de mapa de bits fuera de pantalla se haya creado de la manera correcta. El fondo de este buffer debe ser opaco.

Mi vista tenía anteriormente un fondo PNG que era ligeramente transparente. Podría haber hecho este fondo opaco y renderizado sin problemas, pero como esta vista debe desvanecerse, necesitaba estar respaldado por CALayer, por lo que el texto se procesa una vez correctamente y luego se renderiza sin Alias ​​de píxeles secundarios. .

Aquí está mi código. Parece increíblemente detallado, me encantaría que alguien pudiera ayudarme a reducirlo. Supone que tiene un NSImageView llamado _title y un NSString llamado title.

// Create the attributed string 
NSMutableAttributedString *attStr = [[[NSMutableAttributedString alloc] initWithString: title] autorelease]; 

NSRange strRange = NSMakeRange(0, [attStr length]); 
NSFont *font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize: NSSmallControlSize]]; 

[attStr addAttribute: NSForegroundColorAttributeName value: [NSColor whiteColor] range: strRange]; 
[attStr addAttribute: NSFontAttributeName value: font range: strRange]; 

NSMutableParagraphStyle *paraStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease]; 
[paraStyle setAlignment: NSCenterTextAlignment]; 
[paraStyle setLineBreakMode: NSLineBreakByTruncatingMiddle]; 
[attStr addAttribute: NSParagraphStyleAttributeName value: paraStyle range: strRange]; 

// Set up the image context 
NSUInteger bytesPerPixel = 4; 
NSUInteger bytesPerRow = bytesPerPixel * [_title frame].size.width; 
NSUInteger bitsPerComponent = 8; 
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
// These constants are necessary to enable sub-pixel aliasing. 
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; 

// Create the memory buffer to be used as the context's workspace 
unsigned char *contextBuffer = malloc([_title frame].size.height * [_title frame].size.width * 4); 

CGContextRef context = CGBitmapContextCreate(contextBuffer, 
              [_title frame].size.width, 
              [_title frame].size.height, 
              bitsPerComponent, 
              bytesPerRow, 
              colorSpace, 
              bitmapInfo); 


[NSGraphicsContext saveGraphicsState]; 

[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]]; 


CGContextSetFillColorWithColor(context, CGColorGetConstantColor(kCGColorBlack)); 
CGRect rectangle = CGRectMake(0, 0, [_title frame].size.width,[_title frame].size.height); 
CGContextAddRect(context, rectangle); 
CGContextFillPath(context); 

CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attStr); 

CGContextSetTextPosition(context, 10.0, 10.0); 
CTLineDraw(line, context); 
CFRelease(line); 

// Create a data provider from the context buffer in memory 
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, contextBuffer, bytesPerRow * [_title frame].size.height, NULL); 

// Create an image from the data provider 
CGImageRef imageRef = CGImageCreate ([_title frame].size.width, 
            [_title frame].size.height, 
            bitsPerComponent, 
            bytesPerPixel * 8, 
            bytesPerRow, 
            colorSpace, 
            bitmapInfo, 
            dataProvider, 
            NULL, 
            false, 
            kCGRenderingIntentDefault 
            ); 

// Turn it into an NSImage 
NSImage *newImage = [[[NSImage alloc] initWithCGImage:imageRef size: NSZeroSize] autorelease]; 

CGDataProviderRelease(dataProvider); 
CGColorSpaceRelease(colorSpace); 
CGContextRelease(context); 
CGImageRelease(imageRef); 

free(contextBuffer); 

[NSGraphicsContext restoreGraphicsState]; 

[_title setImage: newImage]; 
+1

Ah - 'CGImageRef imageRef = CGBitmapContextCreateImage (context);' es una mejor manera de obtener una imagen fuera del contexto. –

+1

¡Me alegra oír que lo entendiste! Supongo que la razón por la que no se puede combinar es porque con un fondo transparente no sabría con qué mezclarse, de ahí el motivo de la opacidad. – slf

+0

Bastante, sí. Si solo se tratara de que cada píxel sea ARGB, podría simplemente representarlo como ARGB y haberlo terminado, pero el Alias ​​de subpíxeles realmente usa ARAGAB, es decir, un valor alfa para cada color en lugar de un píxel como un todo. –

-1

¿Cuáles son las posiciones X, Y del texto una vez que se completa con la cadena variable? Tuve un problema similar para UILabel que fue causado por el hecho de que la posición XY del texto eran flotantes. (estaba tratando de centrar los datos variables y los valles XY eran medios píxeles)

Trunqué los valores y se corrigió el texto borroso.

+0

No, ya tenía ese problema ya que la vista del contenedor de control está centrada, por lo que la mitad del tiempo en el cambio de tamaño aparecería en la posición x.5 y se estropearía. Esto es puramente representación de texto, no posicionamiento. –

+0

Problema de subpíxeles en CALayer – xhan

0

No estoy muy seguro de cuáles son sus limitaciones, o por qué tiene que dibujar una capa, pero nuevo en os4.1 entre otras cosas, Core Text se transfirió a iOS desde el escritorio. Probablemente pueda aprovechar sus avanzadas funciones de configuración de tipos para representar glifos a lo largo de líneas o incluso arcos y hacer que la mayor parte del trabajo sea pesado para usted. Es un API de nivel relativamente bajo, pero es muy rápido.

- (void)drawLayer:(CALayer *)theLayer 
     inContext:(CGContextRef)context 
{ 
    CFStringRef string; CTFontRef font; // assuming these exist 

    // Initialize string, font, and context 
    CFStringRef keys[] = { kCTFontAttributeName }; 
    CFTypeRef values[] = { font }; 

    CFDictionaryRef attributes = 
     CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, 
      (const void**)&values, sizeof(keys)/sizeof(keys[0]), 
      &kCFTypeDictionaryCallBacks, 
      &kCFTypeDictionaryValueCallbacks); 

    CFAttributedStringRef attrString = 
     CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); 
    CFRelease(string); 
    CFRelease(attributes); 

    CTLineRef line = CTLineCreateWithAttributedString(attrString); 

    // Set text position and draw the line into the graphics context 
    CGContextSetTextPosition(context, 10.0, 10.0); 
    CTLineDraw(line, context); 
    CFRelease(line); 
} 

Otros ejemplos incluyen CoreTextArcCocoa y CoreTextTest

+0

Esto no funciona porque Core Text tiene las mismas limitaciones que el resto de Quartz. Sin embargo, me puso en el camino correcto. –

0

Así es como el cartel original no quería hacerlo, pero estaba bien para mí tener un fondo opaco ...

cuando hacer la capa:

layer.opaque = YES; 

Luego en

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context 

    [NSGraphicsContext saveGraphicsState]; 
     NSGraphicsContext *nscg = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]; 
     [NSGraphicsContext setCurrentContext:nscg]; 

     NSRect ourframe = self.frame; 
     // white background enables antialiasing 
     [[NSColor whiteColor] setFill]; 
     NSRect ourNSFrame = ourframe; 
     ourNSFrame.origin = NSZeroPoint; 
     NSRectFill(ourNSFrame); 

    [sometext drawInRect:ourNSFrame withAttributes:someattrs]; 

    [NSGraphicsContext restoreGraphicsState]; 
Cuestiones relacionadas