13

I have an NSBezierPath and I want to draw in inset shadow (similar to Photoshop) inside the path.

Is there anyway to do this? Also, I know you can -stroke paths, but can you stroke inside a path (similar to Stroke Inside in Photoshop)?

Update 3

static NSImage * graydient = nil;

if (!graydient) {
    graydient = [[NSImage alloc] initWithSize: NSMakeSize(22, 22)];
    [graydient lockFocus];

    NSGradient * gradient = [[NSGradient alloc] initWithColorsAndLocations: clr(@"#262729"), 0.0f, clr(@"#37383a"), 0.43f, clr(@"#37383a"), 1.0f, nil];
    [gradient drawInRect: NSMakeRect(0, 4.179, 22, 13.578) angle: 90.0f];
    [gradient release];

    [graydient unlockFocus];
}

NSColor * gcolor = [NSColor colorWithPatternImage: graydient];

[gcolor set];

NSShadow * shadow = [NSShadow new];
[shadow setShadowColor: [NSColor colorWithDeviceWhite: 1.0f alpha: 1.0f]];
[shadow setShadowBlurRadius: 0.0f];
[shadow setShadowOffset: NSMakeSize(0, 1)];
[shadow set];

[path fill];

[NSGraphicsContext saveGraphicsState];

[[path pathFromIntersectionWithPath: [NSBezierPath bezierPathWithRect: NSInsetRect([path bounds], 0.6, 0)]] setClip];

[gcolor set];

[shadow setShadowOffset: NSMakeSize(0, 1)];
[shadow setShadowColor: [NSColor blackColor]];
[shadow set];

[outer stroke];

[NSGraphicsContext restoreGraphicsState];

[NSGraphicsContext saveGraphicsState];

[[NSGraphicsContext currentContext] setCompositingOperation: NSCompositeSourceOut];

[shadow set];
[[NSColor whiteColor] set];
[inner fill];

[shadow set];
[inner fill];

[NSGraphicsContext restoreGraphicsState];

Final Result This is my final result. It looks pretty good. I had to change the shadow to White @ 1.0 Alpha to make it work. Even though the shadow alpha norm for menu bar items is 0.5, it doesn't look half bad.

Many thanks go out to Joshua Nozzi.

Community
  • 1
  • 1
Alexsander Akers
  • 15,967
  • 12
  • 58
  • 83
  • Just as an afterthought - it's exactly this kind of stuff where tools like [**PaintCode**](http://www.paintcodeapp.com/) can save you time. Yeah, the price point is a little bit steep if you first look at it but I guess this is a great example how it could have *saved* you some precious time - and thus money.. (and *No*, I'm not affiliated in any way. Just believing in tools and automation saving our time) – Jay Feb 24 '14 at 06:52

1 Answers1

26

I think you can do this by setting clip on the bezier path to use it as a mask and stroking the shadow, then adding a normal stroke if desired.

Update based on updated code:

Here you go. I'm feeling procrastinate-y tonight. :-)

// Describe an inset rect (adjust for pixel border)
NSRect whiteRect = NSInsetRect([self bounds], 20, 20);
whiteRect.origin.x += 0.5;
whiteRect.origin.y += 0.5;

// Create and fill the shown path
NSBezierPath * path = [NSBezierPath bezierPathWithRect:whiteRect];
[[NSColor whiteColor] set];
[path fill];

// Save the graphics state for shadow
[NSGraphicsContext saveGraphicsState];

// Set the shown path as the clip
[path setClip];

// Create and stroke the shadow
NSShadow * shadow = [[[NSShadow alloc] init] autorelease];
[shadow setShadowColor:[NSColor redColor]];
[shadow setShadowBlurRadius:10.0];
[shadow set];
[path stroke];

// Restore the graphics state
[NSGraphicsContext restoreGraphicsState];

// Add a nice stroke for a border
[path setLineWidth:1.0];
[[NSColor grayColor] set];
[path stroke];
Joshua Nozzi
  • 60,946
  • 14
  • 140
  • 135
  • Can you be more specific perhaps? Suggest methods? Cite docs pages? – Alexsander Akers Nov 17 '10 at 17:28
  • See NSBezierPath API reference. A quick search for "clip" shows the -addClip and -setClip methods. Be sure and read the descriptions of these methods and understand the things they reference (winding path rules, etc). You'll want to save the graphics state (NSGraphicsState), do your clipping and shadow drawing, then restore the previous state, then continue drawing normally). By "stroking the shadow" I meant that, after clipping, set the shadow (described in NSShadow API reference), stroke the path, restore the graphics state, then set your normal stroke color and stroke the path again. – Joshua Nozzi Nov 17 '10 at 18:58
  • How can I draw my gradient somewhere in the middle and use `[NSColor clearColor]` so I don't get the white background? – Alexsander Akers Dec 31 '10 at 21:03
  • I'm not sure what you're asking here. What are you seeing and what do you want to see? Can you provide illustrations somewhere? – Joshua Nozzi Dec 31 '10 at 21:32
  • I'm getting a weird 'V' shape under the plus. Any ideas? – Alexsander Akers Jan 02 '11 at 04:09
  • I'm not seeing what you're describing in the image you posted. Maybe the undesired visual effect is lost when it's blown up to the size of the posted image? – Joshua Nozzi Jan 02 '11 at 19:43