【问题标题】:How can I combine CGPathCreateCopyByDashingPath() and CGPathCreateCopyByStrokingPath() to stroke a dashed CGPath on OS X?如何结合 CGPathCreateCopyByDashingPath() 和 CGPathCreateCopyByStrokingPath() 在 OS X 上描边虚线 CGPath?
【发布时间】:2015-10-14 16:09:53
【问题描述】:

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

但是,CGPathCreateCopyByStrokingPath() 接受线条描边参数,如线条上限、线条连接等,而 CGPathCreateCopyByDashingPath() 不接受。我希望能够使用自定义线帽/连接来破折号。

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

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

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

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

我所看到的一切都表明 Core Graphics 能够直接使用 CGContext 执行此操作;例如,Programming with Quartz 一书在其破折号的示例中显示了圆形和方形线帽。有什么理由我不能用独立的 CGPath 做到这一点?

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

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

谢谢。

【问题讨论】:

    标签: macos cocoa core-graphics quartz-graphics


    【解决方案1】:

    我不确定问题出在哪里,真的。你有一个开始的路径;我们称之为A。你打电话给CGPathCreateCopyByDashingPath()A 开辟一条新的道路,它有你想要的潇洒;让我们称之为BB 没有设置特定的行大写/连接,因为这不是路径的属性,而是在抚摸路径时使用的属性。 (想象一下通过从点到点绘制线段来手工制作一条虚线路径;在该路径描述中没有任何地方有上限或连接的概念,只是每个线段的起点和终点。)然后使用B 并调用@987654327 @ 在它上面得到 CB 的笔划的可填充路径,使用特定的线宽/上限/连接特性。最后,使用渐变填充填充C。那不行吗?似乎您知道解决问题所需的所有组件,所以我不确定问题到底出在哪里;你能澄清一下吗?

    【讨论】:

    • 问题是 ByDashing 的文档说“创建了新路径,以便 填充新路径 绘制与使用指定的破折号参数抚摸原始路径相同的像素。”强调我的。除非文档是错误的,否则调用 ByDrawing 后跟 ByStroking 将最终在破折号所在的位置生成一堆小矩形或椭圆。如果这个解释不清楚,我可能应该写一个测试用例并可视化效果,但我会在此期间将其添加到问题中。
    • 啊,我明白你的意思了。如果这真的是 ByDashing 所做的,那么这似乎是一个糟糕的设计 - 它结合了 ByDashing 应该ByStroking 所做的事情。不幸的。是的,如果您对此进行测试,请在此处发布结果,我很想知道。如果文档是正确的,那么我会在 Apple 上提交一个错误,要求提供一个没有损坏的新 API。
    • O...好吧,我似乎无法使用 10.10 上的 CGContext 函数在虚线上设置笔触设置。完全奇怪。我想知道该设施是否被彻底移除,因为使用 Quartz 编程的书说我应该能够...
    • 嗯。嗯,我不知道。 :->
    • 我将把它作为一个单独的问题来问,因为我不知道,从技术上讲,它是一个与 CGPath 对象无关的单独问题。
    【解决方案2】:

    原来CGPathCreateCopyByDashingPath() 的文档是错误的。

    现在,它说

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

    这意味着它使用默认笔画参数生成结果路径。但事实并非如此!相反,您会得到一个新路径,它只是由破折号参数分解的现有路径。您需要调用 CGPathCreateCopyByStrokingPath() 来生成要填充的路径。

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

    再次感谢,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;
    }
    

    【讨论】:

    • 您可以随时向 Apple 提交 Radar(错误报告)以请求更改文档:radar.apple.com
    猜你喜欢
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-04
    • 1970-01-01
    相关资源
    最近更新 更多