如何组合CGPathCreateCopyByDashingPath()和CGPathCreateCopyByStrokingPath()来在OS X上描绘虚线CGPath?

时间:2015-10-14 16:09:54

标签: macos cocoa core-graphics quartz-graphics

Core Graphics有两个函数CGPathCreateCopyByDashingPath()CGPathCreateCopyByStrokingPath(),它们都采用您想要描边的CGPath并将其转换为等效的填充。我需要这样做,所以我可以,例如,用渐变描边一行:调用CGPathCreateCopyByStrokingPath(),将路径加载到CGContext,调用CGContextClip(),然后绘制渐变。

但是,CGPathCreateCopyByStrokingPath()接受线条描边线,线条连接等行描边参数,而CGPathCreateCopyByDashingPath()则不行。我希望能够使用自定义行上限/加入。

特别是,这两个功能在其文档中都有以下内容:

  

创建新路径,以便填充新路径绘制与使用指定的短划线参数描边原始路径相同的像素。

     

创建新路径,以便填充新路径绘制与描边原始路径相同的像素。

强调我的。所以我从中得到的结果是,一旦你调用了任何一个函数,你就会获得一条新的路径,该路径由绑定请求的笔划的线组成。因此,如果我调用ByDashing然后调用ByStroking,第一个将创建一个由一堆小矩形组成的路径,然后第二个将创建形成这些小矩形的周边线的矩形,不是我想要的。 (我可以测试一下并稍后发布图片。)

我所看到的一切都指向Core Graphics能够直接使用CGContext执行此操作;例如,Programming with Quartz book在其简洁的例子中显示了圆形和方形线帽。有没有理由我不能用独立的CGPath做到这一点?

我错过了什么吗?或者我只是坚持这个?

这适用于OS X,不适用于iOS。

感谢。

2 个答案:

答案 0 :(得分:1)

我不确定问题是什么,真的。你有一条开始的道路;我们称之为A。您致电CGPathCreateCopyByDashingPath()A创建一条具有您想要的潇洒的新路径;让我们称之为BB没有设置特定的行上限/连接,因为它不是路径的属性,而是在描边路径时使用的属性。 (想象一下,通过从点到点绘制线段来手工制作虚线路径;在路径描述中没有任何地方存在任何大写或连接的概念,只是每个段的起点和终点。)然后取B并调用CGPathCreateCopyByStrokingPath()上有CB,使用特定线宽/上限/连接特征的C笔划的可填充路径。最后,使用渐变填充填充sandbox = TRUE。这不起作用吗?您似乎知道解决问题所需的所有组件,因此我不确定问题的实际位置;你能澄清一下吗?

答案 1 :(得分:1)

原来CGPathCreateCopyByDashingPath()的文档错误。

现在,它说

  

创建新路径,以便填充新路径绘制与使用指定的短划线参数描边原始路径相同的像素。

这意味着它使用默认笔划参数生成结果路径。但事实并非如此!相反,您将获得一条新路径,该路径只是由划线参数分解的现有路径。您需要致电CGPathCreateCopyByStrokingPath()以生成填充路径。

以下程序包含三个部分。首先,它通过使用CGContext函数而不是CGPath函数进行绘制来显示路径应该是什么样子。其次,它仅使用CGPathCreateCopyByDashingPath()绘制。注意如何抚摸路径不会产生一串破折号的盒子,而是一串破折号。如果你仔细观察,你会看到一条非常小的蓝色疾病,这条线加入了。最后,它调用CGPathCreateCopyByDashingPath()后跟CGPathCreateCopyByStrokingPath(),您将看到填充 会产生正确的输出。

Screenshot

再次感谢,bhaller!我不确定文件应该改变什么,或者如何请求这样的改变。

// 15 october 2015
#import <Cocoa/Cocoa.h>

@interface dashStrokeView : NSView
@end

void putstr(CGContextRef c, const char *str, double x, double y)
{
    NSFont *sysfont;
    CFStringRef string;
    CTFontRef font;
    CFStringRef keys[1];
    CFTypeRef values[1];
    CFDictionaryRef attrs;
    CFAttributedStringRef attrstr;
    CTLineRef line;

    sysfont = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
    font = (CTFontRef) sysfont;     // toll-free bridge

    string = CFStringCreateWithCString(kCFAllocatorDefault,
        str, kCFStringEncodingUTF8);
    keys[0] = kCTFontAttributeName;
    values[0] = font;
    attrs = CFDictionaryCreate(kCFAllocatorDefault,
        keys, values,
        1,
        &kCFTypeDictionaryKeyCallBacks,
        &kCFTypeDictionaryValueCallBacks);
    attrstr = CFAttributedStringCreate(kCFAllocatorDefault, string, attrs);

    line = CTLineCreateWithAttributedString(attrstr);
    CGContextSetTextPosition(c, x, y);
    CTLineDraw(line, c);

    CFRelease(line);
    CFRelease(attrstr);
    CFRelease(attrs);
    CFRelease(string);
}

@implementation dashStrokeView

- (void)drawRect:(NSRect)r
{
    CGContextRef c;
    CGFloat lengths[2] = { 10, 13 };
    CGMutablePathRef buildpath;
    CGPathRef copy, copy2;

    c = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];

    CGContextSaveGState(c);

    putstr(c, "Dash + Stroke With CGContext Functions", 10, 10);
    CGContextMoveToPoint(c, 50, 50);
    CGContextAddLineToPoint(c, 100, 30);
    CGContextAddLineToPoint(c, 150, 70);
    CGContextAddLineToPoint(c, 200, 50);
    CGContextSetLineWidth(c, 10);
    CGContextSetLineJoin(c, kCGLineJoinBevel);
    CGContextSetLineCap(c, kCGLineCapRound);
    CGContextSetLineDash(c, 0, lengths, 2);
    CGContextSetRGBStrokeColor(c, 0, 0, 0, 1);
    CGContextStrokePath(c);
    // and reset
    CGContextSetLineWidth(c, 1);
    CGContextSetLineJoin(c, kCGLineJoinMiter);
    CGContextSetLineCap(c, kCGLineCapButt);
    CGContextSetLineDash(c, 0, NULL, 0);

    CGContextTranslateCTM(c, 0, 100);
    putstr(c, "Dash With CGPath Functions", 10, 10);
    buildpath = CGPathCreateMutable();
    CGPathMoveToPoint(buildpath, NULL, 50, 50);
    CGPathAddLineToPoint(buildpath, NULL, 100, 30);
    CGPathAddLineToPoint(buildpath, NULL, 150, 70);
    CGPathAddLineToPoint(buildpath, NULL, 200, 50);
    copy = CGPathCreateCopyByDashingPath(buildpath, NULL, 0, lengths, 2);
    CGContextAddPath(c, copy);
    CGContextStrokePath(c);
    CGContextAddPath(c, copy);
    CGContextSetRGBFillColor(c, 0, 0.25, 0.5, 1);
    CGContextFillPath(c);
    CGPathRelease(copy);
    CGPathRelease((CGPathRef) buildpath);

    CGContextTranslateCTM(c, 0, 100);
    putstr(c, "Dash + Stroke With CGPath Functions", 10, 10);
    buildpath = CGPathCreateMutable();
    CGPathMoveToPoint(buildpath, NULL, 50, 50);
    CGPathAddLineToPoint(buildpath, NULL, 100, 30);
    CGPathAddLineToPoint(buildpath, NULL, 150, 70);
    CGPathAddLineToPoint(buildpath, NULL, 200, 50);
    copy = CGPathCreateCopyByDashingPath(buildpath, NULL, 0, lengths, 2);
    copy2 = CGPathCreateCopyByStrokingPath(copy, NULL, 10, kCGLineCapRound, kCGLineJoinBevel, 10);
    CGContextAddPath(c, copy2);
    CGContextSetRGBFillColor(c, 0, 0.25, 0.5, 1);
    CGContextFillPath(c);
    CGContextAddPath(c, copy2);
    CGContextStrokePath(c);
    CGPathRelease(copy2);
    CGPathRelease(copy);
    CGPathRelease((CGPathRef) buildpath);

    CGContextRestoreGState(c);
}

- (BOOL)isFlipped
{
    return YES;
}

@end

@interface appDelegate : NSObject<NSApplicationDelegate>
@end

@implementation appDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)note
{
    NSWindow *mainwin;
    NSView *contentView;
    dashStrokeView *view;
    NSDictionary *views;
    NSArray *constraints;

    mainwin = [[NSWindow alloc] initWithContentRect: NSMakeRect(0, 0, 320, 360)
        styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
        backing:NSBackingStoreBuffered
        defer:YES];
    [mainwin setTitle:@"Dash/Stroke Example"];
    contentView = [mainwin contentView];

    view = [[dashStrokeView alloc] initWithFrame:NSZeroRect];
    [view setTranslatesAutoresizingMaskIntoConstraints:NO];
    [contentView addSubview:view];

    views = NSDictionaryOfVariableBindings(view);
    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[view]-|"
        options:0
        metrics:nil
        views:views];
    [contentView addConstraints:constraints];
    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[view]-|"
        options:0
        metrics:nil
        views:views];
    [contentView addConstraints:constraints];

    [mainwin cascadeTopLeftFromPoint:NSMakePoint(20, 20)];
    [mainwin makeKeyAndOrderFront:nil];
}

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
{
    return YES;
}

@end

int main(void)
{
    NSApplication *app;

    app = [NSApplication sharedApplication];
    [app setActivationPolicy:NSApplicationActivationPolicyRegular];
    [app setDelegate:[appDelegate new]];
    [app run];
    return 0;
}