2010-11-27 6 views
9

Ich habe einige variable Text in einem NSTextField, die auf einer CALayer Hintergrundansicht rendert. Da ein CALayer kein Subpixel-Aliasing für die Textrendering eines beliebigen Textes darüber unterstützt, sieht dieser Text abartig aus.So umgehen Sie schlechtes Text-Rendering in Text mit einem CALayer

Ein wenig Googeln zeigt die Gründe, warum dies ist, und dass Text auf einem undurchsichtigen Hintergrund gerendert werden muss, um SPA aktiviert zu haben. Das Rendern auf einen undurchsichtigen Hintergrund möchte ich in diesem Fall möglichst vermeiden. Gibt es eine bessere Problemumgehung?

Ich bin vollständig zugänglich für die Wiedergabe des Textes selbst in eine NSImage, wenn das helfen wird, aber ich kann keine bestätigten Berichte finden, die es wird.

Es sieht absolut gut in Interface Builder, so dass ich weiß, das Geheimnis ist irgendwo in diesem Computer nur anstrengend, um auszusteigen.

+1

Konnten Sie den Code bekanntgeben, der verwendet wurde, um die NSTextView Schicht zu ändern? –

+0

Es gibt sehr wenig tatsächlichen Code, da ich hauptsächlich Dinge in Interface Builder mache. Es gibt ein NSView, das ein NSImageVie –

+0

steuert, das ein NSImageView mit einem halbtransparenten PNG steuert. Der Text sitzt darüber. –

Antwort

3

Abhilfe gefunden. Nichts in Quartz kann scheinbar Text mit Sub-Pixel Aliasing über einem transparenten Hintergrund darstellen. Sie können jedoch Text in einen Bitmap-Puffer im Offscreen-Format rendern, vorausgesetzt, dass der Offscreen-Bitmap-Puffer in der richtigen Weise erstellt wurde. Der Hintergrund dieses Puffers muss undurchsichtig sein.

Meine Ansicht hatte zuvor einen PNG-Hintergrund, der leicht transparent war. Ich hätte diesen Hintergrund einfach ohne Probleme opak und gerendert machen können, aber da diese Ansicht ein- und ausgeblendet werden muss, musste sie CALayer-backed sein, so dass der Text einmal richtig gerendert und anschließend ohne Sub-Pixel Aliasing gerendert wird .

Hier ist mein Code. Es scheint unglaublich wortreich, ich würde es lieben, wenn mir jemand helfen könnte, es abzuschwächen. Es wird davon ausgegangen, dass Sie eine NSImageView namens _title und eine NSString namens title haben.

// 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 (Kontext);' ist eine bessere Möglichkeit, ein Bild aus dem Kontext zu entfernen. –

+1

Freut mich zu hören, dass du es herausgefunden hast! Ich nehme an, der Grund, warum es nicht vermischen kann, ist, weil es mit einem transparenten Hintergrund nicht wissen würde, womit es sich vermischen soll, daher der Grund für die Undurchsichtigkeit. – slf

+0

So ziemlich, ja. Wenn es nur darum geht, dass jedes Pixel ARGB ist, könnte es es einfach als ARGB rendern und damit gemacht haben, aber Sub-Pixel Aliasing verwendet tatsächlich ARAGAB, d. H. Einen Alpha-Wert für jede Farbe und nicht das Pixel als Ganzes. –

-1

Was sind die X-, Y-Positionen des Textes, sobald er seine variable Zeichenfolge ausgefüllt hat? Ich hatte ein ähnliches Problem für eine UILabel, die durch die Tatsache verursacht wurde, dass die XY-Position des Textes floats war. (Es wurde versucht, variable Daten zu zentrieren, und die XY-Werte waren halbe Pixel.)

Ich habe die Werte abgeschnitten und den unscharfen Text korrigiert.

+0

Nein - ich hatte dieses Problem bereits, da die Steuercontaineransicht zentriert ist, also würde die Hälfte der Zeit bei der Größenanpassung an der Position x.5 erscheinen und sich vermasseln. Dies ist reines Text-Rendering, nicht Positionierung. –

+0

Sub-Pixel-Problem auf CALayer – xhan

0

Ich bin mir nicht ganz sicher, was Ihre Grenzen sind, oder warum Sie unbedingt auf eine Ebene zeichnen müssen, aber neu in os4.1 wurde Core Text von Desktop auf iOS portiert. Sie können wahrscheinlich die fortschrittlichen Funktionen zur Typeinstellung nutzen, um Glyphen entlang von Linien oder sogar Bögen zu rendern und das meiste schwere Heben für Sie erledigen zu lassen. Es ist eine relativ Low-Level-API, aber es ist sehr schnell.

- (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); 
} 

Weitere Beispiele sind CoreTextArcCocoa und CoreTextTest

+0

Dies funktioniert nicht, da der Kerntext die gleichen Einschränkungen wie der Rest von Quartz hat. Das hat mich aber auf die richtige Spur gebracht. –

0

Hier ist, wie das ursprüngliche Plakat, es nicht tun wollte, aber es war in Ordnung für mich einen undurchsichtigen Hintergrund zu haben ...

, wenn Sie die Ebene machen:

layer.opaque = YES; 

Dann in

- (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];