Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Graphics: Drawing along a path with a normal gradient

There are a number of resources here and elsewhere on the web regarding how to draw with a gradient - fill or stroke.

However, AFAICT, none addresses the following requirement: how to draw a path with a normal gradient, whereby normal means orthogonal to the path. The net effect could be something like toothpaste or a tube when applied with a dark->light->dark linear gradient. Here is this idea in the case of a round rectangle:

round-rect tube http://muys.net/cadre_blanc.png

(this was hand drawn and the corners are not very good).

In the specific case of the round rect, I think I can achieve this effect with 4 linear gradients (the sides) and 4 radial gradients (the corners). But is there better?

Is there an easy solution for any path?

like image 745
Jean-Denis Muys Avatar asked Feb 07 '11 12:02

Jean-Denis Muys


1 Answers

The only "easy" solution I can think of would be to stroke the path multiple times, reducing the stroke width and changing the color slightly each time, to simulate a gradient.

Obviously, this could be an expensive operation for complex paths so you would want to cache the result if possible.

#define RKRandom(x) (arc4random() % ((NSUInteger)(x) + 1))

@implementation StrokeView

- (void)drawRect:(NSRect)rect 
{
    NSRect bounds = self.bounds;

    //first draw using Core Graphics calls
    CGContextRef c = [[NSGraphicsContext currentContext] graphicsPort];

    CGMutablePathRef path = CGPathCreateMutable();

    CGPathMoveToPoint(path, NULL, NSMidX(bounds), NSMidY(bounds));
    CGContextSetMiterLimit(c,90.0);
    CGContextSetLineJoin(c, kCGLineJoinRound);
    CGContextSetLineCap(c, kCGLineCapRound);

    for(NSUInteger f = 0; f < 20; f++)
    {
        CGPathAddCurveToPoint(
                              path,
                              NULL,
                              (CGFloat)RKRandom((NSInteger)NSWidth(bounds)) + NSMinX(bounds), 
                              (CGFloat)RKRandom((NSInteger)NSHeight(bounds)) + NSMinY(bounds),
                              (CGFloat)RKRandom((NSInteger)NSWidth(bounds)) + NSMinX(bounds), 
                              (CGFloat)RKRandom((NSInteger)NSHeight(bounds)) + NSMinY(bounds),
                              (CGFloat)RKRandom((NSInteger)NSWidth(bounds)) + NSMinX(bounds), 
                              (CGFloat)RKRandom((NSInteger)NSHeight(bounds)) + NSMinY(bounds)
                              );
    }

    for(NSInteger i = 0; i < 8; i+=2)
    {
        CGContextSetLineWidth(c, 8.0 - (CGFloat)i);
        CGFloat tint = (CGFloat)i * 0.15;

        CGContextSetRGBStrokeColor (
                                    c,
                                    1.0,
                                    tint,
                                    tint,
                                    1.0
                                    );
        CGContextAddPath(c, path);
        CGContextStrokePath(c);
    }

    CGPathRelease(path);

    //now draw using Cocoa drawing
    NSBezierPath* cocoaPath = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(self.bounds, 20.0, 20.0) xRadius:10.0 yRadius:10.0];
    for(NSInteger i = 0; i < 8; i+=2)
    {
        [cocoaPath setLineWidth:8.0 - (CGFloat)i];
        CGFloat tint = (CGFloat)i * 0.15;
        NSColor* color = [NSColor colorWithCalibratedRed:tint green:tint blue:1.0 alpha:1.0];
        [color set];
        [cocoaPath stroke];
    }
}

@end

sample output

like image 116
Rob Keniger Avatar answered Nov 18 '22 18:11

Rob Keniger