如何在CALayer支持的文本中处理不良文本呈现

时间:2010-11-27 19:32:34

标签: objective-c core-animation text-rendering

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

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

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

它在Interface Builder中看起来非常好,所以我知道这台计算机内部的秘密只是紧张而已。

4 个答案:

答案 0 :(得分:3)

找到了解决方法。 Quartz中的任何内容都无法在透明背景上呈现带有Sub-Pixel Aliasing的文本。但是,您可以将文本渲染到屏幕外位图缓冲区,前提是已经以正确的方式创建了屏幕外位图缓冲区。此缓冲区的背景必须是不透明的。

我之前的视图有一个稍微透明的PNG背景。我可以简单地使这个背景不透明并且没有问题地渲染它,但是由于这个视图需要淡入淡出,它需要被CALayer支持,所以文本渲染一次,然后渲染没有子像素别名

这是我的代码。它似乎令人难以置信的冗长,如果有人能帮助我减少它,我会喜欢它。它假设您有一个名为_title的NSImageView和一个名为title的NSString。

// 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 :(得分: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

答案 2 :(得分: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];

答案 3 :(得分:-1)

文本的X,Y位置一旦得到它的变量字符串就填充了什么?我对UILabel有类似的问题,这是由文本的XY位置浮动引起的。 (它试图将可变数据居中,而XY值是半像素)

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