Как обойти плохую визуализацию текста в тексте, поддерживаемом CALayer - PullRequest
10 голосов
/ 27 ноября 2010

У меня есть переменный текст в NSTextField, который отображается в CALayer фоновом режиме. В CALayer не поддерживает субпиксельное сглаживание для отображения текста любого текста поверх него, этот текст выглядит мусор.

Немного поиска в Google показывает причины, по которым это происходит, и этот текст должен быть отображен на непрозрачном фоне, чтобы включить SPA. Рендеринг на непрозрачный фон - это то, чего я бы хотел избежать, если это вообще возможно в этом случае. Есть ли лучший обходной путь?

Я полностью поддаюсь преобразованию текста в NSImage, если это поможет, но я не могу найти подтвержденных отчетов, что это будет.

В Интерфейсном Разработчике все выглядит отлично, так что я знаю, что секрет где-то внутри этого компьютера, просто напрягается, чтобы выйти.

Ответы [ 4 ]

3 голосов
/ 21 декабря 2010

Обходной путь найден.Ничто в Кварце не может визуализировать текст с Sub-Pixel Aliasing поверх прозрачного фона, по-видимому.Однако вы МОЖЕТЕ отрисовать текст в буфере закадрового изображения, при условии, что буфер закадрового изображения был создан правильным образом.Фон этого буфера должен быть непрозрачным.

Мой вид ранее имел фон 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];
0 голосов
/ 09 октября 2013

Вот как оригинальный плакат не хотел этого делать, но у меня было нормально иметь непрозрачный фон ...

при создании слоя:

    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];
0 голосов
/ 07 декабря 2010

Я не совсем уверен, каковы ваши ограничения или почему вам абсолютно необходимо рисовать на слое, но, кроме прочего, новое в 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);
}

Другие примеры включают CoreTextArcCocoa и CoreTextTest

0 голосов
/ 02 декабря 2010

Каковы позиции X, Y текста, когда он заполняет переменную строку?У меня была похожая проблема для UILabel, которая была вызвана тем фактом, что XY-позиция текста была плавающей.(он пытался центрировать переменные данные, а значения XY составляли половину пикселей)

Я обрезал значения и исправил размытый текст.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...