10

我在背景视图NSTextField上呈现了一些可变文本。CALayer由于 aCALayer不支持对其上的任何文本进行文本渲染的子像素别名,因此该文本看起来很垃圾。

一些谷歌搜索揭示了为什么会这样,并且必须将文本渲染到不透明的背景上才能启用 SPA。在这种情况下,如果可能的话,我想避免渲染到不透明的背景上。有更好的解决方法吗?

我完全可以自己将文本呈现为NSImage,如果这会有所帮助,但我找不到任何确认的报告。

它在 Interface Builder 中看起来非常好,所以我知道秘密就在这台计算机内部的某个地方,只是在努力摆脱。

4

4 回答 4

3

找到解决方法。Quartz 中似乎没有任何东西可以在透明背景上渲染带有子像素锯齿的文本。但是,您可以将文本渲染到屏幕外位图缓冲区,前提是已以正确的方式创建了屏幕外位图缓冲区。此缓冲区的背景必须是不透明的。

我的视图以前有一个稍微透明的 PNG 背景。我本可以简单地使这个背景不透明并毫无问题地呈现给它,但是由于这个视图需要淡入和淡出,它需要支持 CALayer,因此文本正确呈现一次,然后在没有子像素锯齿的情况下随后呈现.

这是我的代码。它似乎非常冗长,如果有人可以帮助我减少它,我会喜欢它。它假设你有一个 NSImageView 调用_title和一个 NSString 调用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];
于 2010-12-20T21:53:12.080 回答
0

我不太确定您的限制是什么,或者为什么您绝对必须绘制到一个图层,但在os4.1中的新功能,Core Text已从桌面移植到 iOS。您可能可以利用它的高级类型设置功能沿线甚至弧线渲染字形,并让它为您完成大部分繁重的工作。它是一个相对低级的 API,但速度非常快。

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

其他示例包括CoreTextArcCocoaCoreTextTest

于 2010-12-07T19:42:43.573 回答
0

这是原始海报不想这样做的方式,但我可以有一个不透明的背景......

当你制作图层时:

    layer.opaque = YES;

然后在

  - (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];
于 2013-10-09T15:39:18.037 回答
-1

填充变量字符串后,文本的 X、Y 位置是什么?我有一个类似的问题,UILabel这是由于文本的 XY 位置是浮动的。(它试图将变量数据居中,而 XY 值是半像素)

我截断了这些值并修复了模糊的文本。

于 2010-12-02T18:33:37.677 回答