将 CGPathRef 拆分为多个路径

Split a CGPathRef into multiple paths

我试图通过 CGPathRefs 将文本分成所有不同的部分。例如,L 有一条路径,而 ?有两个(点和其余部分)。

目前,我可以将每个字母作为路径获取,但是在“?”的示例中该路径同时包含点和其余部分。我想把它们分开,但我似乎无法让它发挥作用。

为了将文本划分为路径,我使用了堆栈溢出的一些答案,效果很好,这是我将每个字母作为路径的部分。这里我也运行它通过"CGPathApply"函数来查看每个单独的点

CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
NSMutableArray *pathElements = [NSMutableArray array];
CGPathApply(letter, (__bridge void * _Nullable)(pathElements), getPointsFromBezier);

然后我像这样查看 getPointsFromBezier 中的每个点:

void getPointsFromBezier(void *info, const CGPathElement *element){
    CGPoint *p = element->points;
    NSString *name;
    switch (element->type) {
        case kCGPathElementMoveToPoint: name = @"kCGPathElementMoveToPoint"; break;
        case kCGPathElementAddLineToPoint: name = @"kCGPathElementAddLineToPoint"; break;
        case kCGPathElementAddQuadCurveToPoint: name = @"kCGPathElementAddQuadCurveToPoint"; break;
        case kCGPathElementAddCurveToPoint: name = @"kCGPathElementAddCurveToPoint"; break;
        case kCGPathElementCloseSubpath: name = @"kCGPathElementCloseSubpath"; break;
        default: name = @"default"; break;
    }
    NSLog(@"Type: %@ || Point: %@", name, NSStringFromCGPoint(*p));
}

这给了我这样的输出:

Type: kCGPathElementMoveToPoint || Point: {11.75, 0}
Type: kCGPathElementAddLineToPoint || Point: {11.75, 9.729}
Type: kCGPathElementAddQuadCurveToPoint || Point: {11.75, 11.26}
..
..
Type: kCGPathElementAddQuadCurveToPoint || Point: {13.00, 10.52}
Type: kCGPathElementAddLineToPoint || Point: {13.00, 0}
Type: kCGPathElementCloseSubpath || Point: {13.00, 0}
Type: kCGPathElementMoveToPoint || Point: {12.75, 18.75}
Type: kCGPathElementAddQuadCurveToPoint || Point: {11.5, 18.94}
Type: kCGPathElementAddLineToPoint || Point: {11.5, 19.95}
Type: kCGPathElementAddQuadCurveToPoint || Point: {11.5, 20.41}
..
..
Type: kCGPathElementAddQuadCurveToPoint || Point: {12.75, 18.169}
Type: kCGPathElementCloseSubpath || Point: {12.75, 18.169}

我在这里输入的字符是ก็,我们可以清楚地看到它在输出中有两个不同的路径(即两个kCGPathElementMoveToPoint和两个kCGPathElementCloseSubpath)。

我不确定如何从中正确或很好地创建两个单独的 CGPathRef。

其实写题的时候我差点就解决了

我创建了一个静态可变 CGPath,然后在通过 CGPathApply 调用的函数中添加点。像这样:

static CGMutablePathRef path;

void getPointsFromBezier(void *info, const CGPathElement *element){
    CGPoint *p = element->points;

    if ( element->type == kCGPathElementMoveToPoint ){
        path = CGPathCreateMutable();
        CGPathMoveToPoint(path, nil, p->x, p->y);
    }
    else if ( element->type == kCGPathElementAddLineToPoint ){
        CGPathAddLineToPoint(path, nil, p->x, p->y);
    }
    else if ( element->type == kCGPathElementAddQuadCurveToPoint ){
        CGPathAddQuadCurveToPoint(path, nil, p->x, p->y, p->x, p->y);
    }
    else if ( element->type == kCGPathElementAddCurveToPoint ){
        // ...
    }
    else if ( element->type == kCGPathElementCloseSubpath ){
        CGPathCloseSubpath(path);
        [((__bridge NSMutableArray*)info) addObject:(__bridge id _Nonnull)(path)];
        path = nil;
        CGPathRelease(path);
    }
}

然后我简单地循环遍历原始方法中的每个路径并绘制它们而不是原始的"letter" CGPath。

CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
NSMutableArray *pathElements = [NSMutableArray array];
CGPathApply(letter, (__bridge void * _Nullable)(pathElements), getPointsFromBezier);

for (id e in pathElements) {
    CGContextSaveGState(ctx);
    CGContextTranslateCTM(ctx,position.x,0 - 40 + CGRectGetMidY(rect) - self.font.descender + position.y);
    CGContextScaleCTM(ctx, 1, -1);
    CGContextAddPath(ctx, (CGPathRef)e);
    [[self r] setFill];
    CGContextFillPath(ctx);
    CGContextRestoreGState(ctx);
}

CGPathRelease(letter);

这有点像。一个问题是我没有得到正确的曲线,因此生成的路径不像它们应该的那样平滑。如果我解决了我会更新

这是目前的结果,您可以看到与底部的原始图像相比,图像有点块状。然而,它们有两种不同的颜色,这是最初要解决的问题。

UPDATE:我发现每个点(元素->点)都包含剩余数据(x 和 y's)以正确绘制点。例如:

if ( element->type == kCGPathElementAddQuadCurveToPoint ){
    CGPoint *p0 = &points[0]; // Control points for bezier path
    CGPoint *p1 = &points[1]; // Actual points
    CGPathAddQuadCurveToPoint(path,
                              nil,
                              p0->x,
                              p0->y,
                              p1->x,
                              p1->y);
}

这将正确绘制每个字形。