How to fill a path and keep the path line

Discussion in 'Mac Programming' started by John Baughman, Sep 30, 2009.

  1. macrumors member

    Joined:
    Oct 27, 2003
    #1
    I asked this question differently in the iPhone programming forum without any answer.

    I have a path that I am stroking with a dashed line set to a width of 2. If I fill the path with either a solid fill or with a linear gradient I lose the dashed line of the path.

    How can I fill the path and still see the stroked line?

    Code:
    
    CGContextBeginPath(context);
    .
    .
        //create the path
    .
    .
    CGContextClosePath(context);
    
    [[UIColor colorWithRed:0.808 green:0.58 blue:0.255 alpha:1.0] setFill];
    CGContextFillPath(context);
    
    [[UIColor blackColor] setStroke];
    CGContextSetLineWidth(context, 2.0);
    CGFloat dash1[] = {5.0, 2.0};
    CGContextSetLineDash(context, 0.0, dash1, 2);	
    
    CGContextStrokePath(context);
     
  2. macrumors 6502

    Joined:
    Jan 3, 2009
    #2

    Have you tried doing the fill first, then the dashed line?
     
  3. thread starter macrumors member

    Joined:
    Oct 27, 2003
    #3
    hmmm. Isn't that how I am doing it in the example code I included in my first post? If not, how do I reset the order?
     
  4. macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #4
    Offhand that looks fine to me. Not sure what's going wrong. Try making your lengths and widths really big so it's extremely obvious when you get it drawing? :)
     
  5. thread starter macrumors member

    Joined:
    Oct 27, 2003
    #5
    Yes I had done that, set it to 10. Shifting back and forth between filled and not filled you can see that the fill is overlapping where the line should be. In other words the fill fills out to the outer edge of the path line.

    Same thing with the linear gradient where the clip clips out to the outer edge of the path line.
     
  6. thread starter macrumors member

    Joined:
    Oct 27, 2003
    #6
    Ok I figured out a solution to the problem with regard to the fill. The path line will be display if I use

    CGContextDrawPath(context, kCGPathFillStroke);

    instead of...

    CGContextStrokePath(context);

    Still cannot figure out the gradient fill. CGContextDrawLinearGradient does not have an option similar to kCGPathFillStroke. Here is what my code looks like for a gradient fill...

    Code:
    CGGradientRef gradientFill;
    CGColorSpaceRef colorSpace;
    size_t num_locations = 2;
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat components[8] = { 0.95, 0.30, 0.30, 1.0,  // Start color
    		0.93, 0.94, 0.30, 1.0 }; // End color
    		
    colorSpace = CGColorSpaceCreateDeviceRGB();
    gradientFill = CGGradientCreateWithColorComponents (colorSpace, components,											 locations, num_locations);
    
    CGPoint myStartPoint, myEndPoint;
    myStartPoint.x = 0.0; //gradientXStart-20;
    myStartPoint.y = rect.size.height / 2.0;
    myEndPoint.x = rect.size.width; //gradientXEnd+20;
    myEndPoint.y = rect.size.height / 2.0;
    		
    [[UIColor blackColor] setStroke];
    CGContextSetLineWidth(context, 2.0);
    if(border.selectedSegmentIndex	== 1){
    	CGContextSetLineDash(context, 0.0, dash1, 2);
    			
    }
    
    CGContextClip(context);		
    CGContextDrawLinearGradient (context, gradientFill, myStartPoint, myEndPoint, 0);
    CGColorSpaceRelease(colorSpace);
    
    //CGContextStrokePath(context);
    //CGContextDrawPath(context, kCGPathFillStroke);
    
    I tried using both CGContextStrokePath and CGContextDrawPath and neither gives me the line path.

    I did just learn that I can leave out a path draw or stroke command and still get the polygon, which I assume is just the gradient in the clipped polygon. I would have thought that then calling the draw or stroke path command would draw the path over the gradient giving it the border I am looking for.
     
  7. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #7
    I would use CGPaths instead of manipulating the path directly on the context.

    Here's how I did it (you'll obviously want to modify the path, and replace the NS* classes with the UI* ones.):

    Code:
    // create the path
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, 10.0, 10.0);
    CGPathAddLineToPoint(path, NULL, 100.0, 10.0);
    CGPathAddLineToPoint(path, NULL, 100.0, 100.0);
    CGPathAddLineToPoint(path, NULL, 10.0, 100.0);
    CGPathAddLineToPoint(path, NULL, 10.0, 10.0);
    
    // setup the gradient
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat components[8] = {
    	0.95, 0.30, 0.30, 1.0,  // Start color
    	0.93, 0.94, 0.30, 1.0	// End color
    };
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradientFill = CGGradientCreateWithColorComponents (colorSpace, components, locations, 2);
    
    // setup gradient points
    CGRect pathRect = CGPathGetBoundingBox(path);
    CGPoint myStartPoint, myEndPoint;
    myStartPoint.x = CGRectGetMinX(pathRect);
    myStartPoint.y = CGRectGetMinY(pathRect);
    myEndPoint.x = CGRectGetMaxX(pathRect);
    myEndPoint.y = CGRectGetMinY(pathRect);
    
    // draw the gradient
    CGContextAddPath(context, path);
    CGContextSaveGState(context);
    CGContextClip(context);
    CGContextDrawLinearGradient (context, gradientFill, myStartPoint, myEndPoint, 0);
    CGContextRestoreGState(context);
    
    // draw the dash
    CGContextAddPath(context, path);
    [[NSColor blackColor] setStroke];
    CGContextSetLineWidth(context, 2.0);
    CGFloat dash1[] = {5.0, 2.0};
    CGContextSetLineDash(context, 0.0, dash1, 2);
    CGContextStrokePath(context);
    
    // cleanup
    CGColorSpaceRelease(colorSpace);
    CGGradientRelease(gradientFill);
    CGPathRelease(path);
    The key is to use CGContextAddPath() which adds the path back onto the context. I guess CG removes it after a fill/stroke.

    Here's how it looks (draw inside an OS X window):

    path.png
     
  8. thread starter macrumors member

    Joined:
    Oct 27, 2003
    #8
    Fantastic. That looks great. I looked at CGContextAddPath but did not recognize that with it I could add paths to a context created as CGPaths outside of a context. Good stuff.

    A quick question. Why do you save the state before you clip and draw the gradient. What is actually being done when you save and restore a context's state? That is really fuzzy to me.

    Thanks,

    John
     
  9. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #9
    Usually saving/restoring the graphics state is done when you're clipping, adding a shadow or affine transformation, or other things that affect subsequent draws. More info at Graphics Contexts (see the Graphics State Information section).

    So in this case, I save/restore it before drawing the dashed line, because the dashed line would be clipped also. Stroked paths are drawn centered on the path, not inside or outside it. If you remove those save/restore calls, you'll see what I mean when the path is stroked.
     
  10. macrumors newbie

    Joined:
    Apr 26, 2013
    #10



    How can I draw a gradient line on iPad.
    Can any one please help me .
    Thanks in advance.
     
  11. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #11
    Apple can help you.

    Start Xcode. Press Command-Shift-2. Click on "Documentation". Enter "gradient" in the search box.
     
  12. macrumors 68000

    Joined:
    Jan 28, 2003
    Location:
    Less lost in L.A.
    #12
    You start by doing some research on gradients.

    Next, when posting a more descriptive question to MR, you begin a new thread. Hijacking an old one for what is an unrelated question is not good etiquette.

    When you come back to post your question, state what you have tried and your intended end goal.
     

Share This Page