自定义 CTLine 结构

Custom CTLine struct

我正在为我的项目创建自定义 CTLine 结构。这是一个非常简单的实现,因为它不使用 CFAttributeString 来创建行。整条线只有一种颜色,所有字形都具有相同的大小。但是CFString越长,创建它的时间越长,比CTLineCreateWithAttributedString()慢。

#import <Cocoa/Cocoa.h>

#define sc (CFStringRef)

    struct Line {
        CGGlyph*line_glyphs;
        CGPoint*point;
        CTFontRef font;
        int length;
    };


    typedef  struct Line* LineRef;

    LineRef create(CFStringRef str, CTFontRef font);

    @interface View : NSView
    {
        LineRef line;
        CTLineRef l;
        CTFontRef font;
        CGFontRef font2;
        CFMutableStringRef string;

    }
 @end



 #import "View.h"
    #include <time.h>


    LineRef create(CFStringRef str, CTFontRef font){

        LineRef line = malloc(sizeof(struct Line));
        long length = CFStringGetLength(str);

        CGGlyph* gl = malloc(sizeof(CGGlyph)*length);
        CGPoint* points = malloc(sizeof(CGPoint)*length);
        CGRect rects[length];
        UniChar buffer[length];
        CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
        CTFontGetGlyphsForCharacters(font, buffer, gl, length);
        CTFontGetBoundingRectsForGlyphs(font, kCTFontOrientationHorizontal, gl, rects, length);
        int x_offset  = 0;
        int y = 200;
        CGPoint temp;
        for(int i = 0;i<length;i++)
        {
            temp = CGPointMake(x_offset , y);
            x_offset += rects[i].size.width + (rects[i].size.width == 0)*10;

            points[i] = temp;
        }

        line->line_glyphs = gl;
        line->point = points;
        line->font = font;
        line->length = length;
        return line;
    }



    @implementation View


    -(void)awakeFromNib{
        font = CTFontCreateWithName(CFSTR("Comic Sans MS"), 30, 0);
        font2= CGFontCreateWithFontName(CFSTR("Comic Sans MS"));
        line = create(CFSTR(""), font);
        string = CFStringCreateMutable(kCFAllocatorDefault, 0);
        [[self window] makeFirstResponder:self];


    }



    -(void)keyDown:(NSEvent *)event{
        static int index = 0;

        NSString* i = [event characters];

        CFStringAppend(string,sc  i);
        CFMutableAttributedStringRef s = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
        CFAttributedStringReplaceString(s, CFRangeMake(0, 0), string);
        CFAttributedStringSetAttribute(s, CFRangeMake(0, CFStringGetLength(string)), kCTFontAttributeName, font);
        CFAbsoluteTime t1 = CFAbsoluteTimeGetCurrent();
        l = CTLineCreateWithAttributedString(s);
        CFAbsoluteTime t2 = CFAbsoluteTimeGetCurrent();
        double d1 = t2-t1;
        CFAbsoluteTime T1 = CFAbsoluteTimeGetCurrent();
        line = create(string, font);
        CFAbsoluteTime T2 = CFAbsoluteTimeGetCurrent();
        double d2 = T2 - T1;
        printf("test:%i core text: %f       my implem : %f \n",index, d1,d2);



        index++;
    }
@end

并输出:

  test:0 core text: 0.000761       my implem : 0.000016 
    test:1 core text: 0.000047       my implem : 0.000029 
    test:2 core text: 0.000041       my implem : 0.000027 
    test:3 core text: 0.000045       my implem : 0.000032 
    test:4 core text: 0.000045       my implem : 0.000032 
    test:5 core text: 0.000046       my implem : 0.000034
    ...
    test:176 core text: 0.000068     my implem : 0.000151 
    test:177 core text: 0.000084     my implem : 0.000171 
    test:178 core text: 0.000099     my implem : 0.000230 
    test:179 core text: 0.000061     my implem : 0.000145 
    test:180 core text: 0.000071     my implem : 0.000224 
    test:181 core text: 0.000057     my implem : 0.000149

您会看到前几个调用比创建 CTLine 快,但我的实现开始需要更多时间来完成工作。 也许是多次 CTFontGetGlyphsForCharacters() 调用错误?你能提供一些建议来加快这段代码的速度吗?

tl;dr 在必要时使用 CTLineCreateWithAttributedString() 并继续;您的管道中的其他地方存在更大的性能问题。 CGContextDrawPath()才是你真正的克星。

作为凭据,我编写了一个 iOS 应用程序,允许 OSM's entire dataset 向下渲染并显示在 iPhone 上 - 一直到人行道和建筑物。它看起来像这样(注意小径的所有小脚都是字体中的字形):

我的管道看起来有点像这样:

  1. 检查每个字符串的边界框并尽可能丢弃
  2. 对剩余字符串调用 CTLineCreateWithAttributedString()
  3. 迭代每个 CTRun 并使用 CTFontCreatePathForGlyph() 获取每个字形路径。
  4. 检查每个路径的边界框并尽可能丢弃。
  5. 调用CGContextDrawPath()剩余路径

第 1 步和第 4 步是需要优化的地方。绘制波浪线(字形)是计算密集型的,没有办法解决这个问题。唯一的制胜法宝就是不下 ¹.

(要获得更多优化乐趣,请查看 MinimumRubber,特别是 MRPathMetrics 函数,可用于避免昂贵的基于 CGPath 的函数)