Inner Shadow or workarounds


#1

Hey all,

So I know there’s the DropShadow which works great, but if you’d like an inner shadow, has anyone come up with some clever workaround (unless I’ve missed something in the JUCE classes!)? I tried having a rectangle and then I drew a line at the top and DropShadowed that, but the results weren’t great unfortunately!

I also found a thread were Vinnie had done some Photoshop-like effects, but seemed as though it hadn’t been updated for a while

Best,
Tom

EDIT: After some more fiddling around I managed to get it quite good looking with the method above. But I’ll gladly take other ideas if someone has any!


#2

I needed this as well. After spending a LOT of time with Vinnie’s effects (re-writing a lot of it to work with current JUCE and to meet my needs since it’s pretty old) I figured it wasn’t worth the hassle and went to write my own quick and dirty solution. What I came up with is (in my opinion) fairly clever and simple.

Start with the JUCE’s drop shadow technique:

  1. Create a single channel mask of the component shape
  2. Gaussian blur it
  3. Draw it underneath the component

To get an inner blur, replace what happens after step 2 with some different operations:

  1. Create a single channel mask of the component shape
  2. Gaussian blur it, keeping an original copy of the unblurred mask.
  3. Invert the gaussian blurred mask.
  4. Multiply the blurred/inverted mask by the original uninverted/unblurred mask. This will remove all the blur around the edges of the component shape in the blurred/inverted mask, leaving only the inside gradient.
  5. Overlay the result on top of the component the same way as normally done with the normal drop shadow underneath the component.

It ended up being pretty simple (I just copied the drop shadow image processing code and modified it), and it performs well and looks good.

If I had to do it all over again though, I would go for a more general solution and use something like OpenCV by people who know a lot more about image processing then I do.


#3

Thanks Jonathon!

For now I decided to go with my approach, but by the looks of it, your approach would probably generate better results, so definitely going to try this out if I change my mind. But for now I felt that I could do without the hassle! :slight_smile:

Tom


#4

I’ve come up with a pretty simple way of drawing inner shadows, however it requires you to specify the outline of the object you want to cast the inner shadow on as a Path.

It works by cutting out the shape to cast the inner shadow on from a larger rectangle, essentially inverting the path, and then using the cutout to cast a DropShadow on the target shape. To avoid the rectangle around the cutout drawing outside of the target shape, I reduce the Graphics object’s clip region to the target shape. Easy as that.

void drawInnerShadow(Graphics &g, Path target) {
	// resets the Clip Region when the function returns
	Graphics::ScopedSaveState saveState(g);

	// invert the path's fill shape and enlarge it,
	// so it casts a shadow
	Path shadowPath(target);
	shadowPath.addRectangle(target.getBounds().expanded(10));
	shadowPath.setUsingNonZeroWinding(false);

	// reduce clip region to avoid the shadow
	// being drawn outside of the shape to cast the shadow on
	g.reduceClipRegion(target);

	DropShadow ds(shadowColor, 1, {0, 1});
	ds.drawForPath(g, shadowPath);
}

Result for a Path with a Rounded Rectangle:
35

Note: you’ll have to replace the shadowColor variable with whatever color you want to use for the shadow.

Also, depending on the Path you use, you may have to remove the line shadowPath.setUsingNonZeroWinding(false);. I haven’t found a consistency in when that is required, yet.