Photoshop Layer Effects for JUCE Controls!

This code is not finished yet. But soon!!

Oh TheVinn, are you The Eye In The Sky ? Man, you read my mind, and you wrote everything I needed :smiley:

Hope you’ll able to finish this project that is a very positive point for many graphics projects.

Respect for your work and your code quality.

I’ve had a couple of breakthroughs with some tricky parts so I’m back on this project and making progress. If you pull the latest version you will see fast, real time updating when changing effect settings. And more importantly, I finally have the correct algorithm for Outer Glow, Softer (which is also used for Drop Shadow, and almost everything else).

If you look at Outer Glow, Softer (uncheck the “precise” checkbox) you can adjust spread and size and they have the right effect. Ditto for Drop Shadow. This was previously missing because there were many details to the implementation that I had not figured out. From here on in it should be just a matter of busywork until this is done:

[attachment=0]LayerEffects.png[/attachment]

Cheers Vinn, this all looks like great stuff. It still doesn’t run out of the box on OSX though. First of all I think “graphics/vf_LayerStyles.h” needs to be included after “graphics/vf_DistanceTransform.h” in “vf_unfinished.h”, moving it to the bottom of the list lets the project build.

Upon running the app however I still get the /* If this goes off it means you aren't using the software renderer for your Image!. LayerGraphics only works with the software renderer! */ jassert (renderer != nullptr); assertion and then bad access crash. This is happening on the “Background Thread” and CustomLookAndFeel::createGraphicsContext never gets called. I know the call to “setCurrentRenderingEngine” is present which should enforce the software renderer anyway but maybe this background thread starts running before the software renderer is enforced?

On a side note I know you don’t use a juce_module_info file in your modules preferring to just add the sources directly to the project. This does however make it quite hard to debug code as a lot of the sources are not directly available in the project. If you added this file and then the modules to the “juce/modules” directory in the project they could be automatically added (as browsable) to every project made with vflib. The JuceLibraryCode directory is automatically added to the header search paths by the Intojucuer so you wont have to manually add these paths. I know you probably want to keep these separate and some progress needs to be made on the Introjucer in order to specify multiple module locations but I think it would make your modules slightly easier to use. Anyway, just a thought from an OSX perspective. Thanks again.

No surprise there, its largely untested.

Its not really practical for me to put VFLib modules into the juce/modules directory, because I use “git-subtree” to bring in external repositories. I understand the problem you are facing, and my solution is simply to add the appropriate static library to the workspace or solution. VFLib and JUCE both have static libraries. This is why I am quite insistent about Jules keeping the static library for JUCE and not discarding it (“Introjucer REQUIRED for practical development”).

If you just add VFLib and JUCE static libraries to your workspace it will become much easier to debug and browse sources.

This is all fixed up now, if you would like to give it a whirl.

Great! I can confirm this now build and runs on OSX. Some really exciting stuff in here. I know its still in development so not everything is currently active but I can’t seem to change the colours with the colour selector. When I press escape to close the window the colour remains as it was, black or white usually.

Still, keep up the good work!

Yeah I didn’t really do much else except pop the dialog.

This is all fixed up now, if you would like to give it a whirl.[/quote]

Yep works on OSX now. Thank you :slight_smile:

Are there any plans when this will be merged to the master branch? I can’ wait for simple white dropshadows on text
[/quote]

It won’t be merged to the master branch for quite some time, because there is still a fair amount of implementation that needs to be finished.

If you want effects on text you will have to either use the software renderer, or your component will need to draw fully opaque to its entire bounding rectangle.

Ah ok, damn, I was hoping for some CSS like textshadow effects but fully opaque labels are not an option.

Well if you use the software renderer then this is easily accomplished with the LayerGraphics context.

In the future I may explore the possibility of applying these effects without requiring either the software renderer or a fully opaque Component but the limitation will be that only the normal blend mode will be supported.

Hi Vinnie,

Little sample of a button using vf::LayerEffects & vf::FreeType :

I’ve not update this project since one month, so I don’t know if you’ve finish the LayerOptions, particulary the GradientOverlay, so the result in not ‘exactly’ the same that in Photoshop.

The commented part is the GradientOverlay that I can’t use directly.

//=============================================================================
#ifndef __BUTTERSCOTCHUI_H_83A08092__
#define __BUTTERSCOTCHUI_H_83A08092__
// ============================================================================
#include "includes.h"
//=============================================================================
#include "vf_unfinished.h"
//=============================================================================
namespace vf {
//=============================================================================
static void setButtonDepthOptions (LayerGraphics::Options &opt)
{
	// General
	opt.general.opacity = 0.35;
	opt.general.groupInteriorEffects = false;
	opt.fill.opacity = 1.0;
	opt.fill.mode = BlendMode::modeNormal;

	// Drop Shadow
	opt.dropShadow.active = true;
	opt.dropShadow.mode = BlendMode::modeOverlay;
	opt.dropShadow.colour = Colours::white;
	opt.dropShadow.opacity = 0.35;
	opt.dropShadow.angle = vf::degreesToRadians <double> (90);
	opt.dropShadow.distance = 4;
	opt.dropShadow.spread = 0.0;
	opt.dropShadow.size = 1;

	// Inner Shadow
	opt.innerShadow.active = true;
	opt.innerShadow.mode = BlendMode::modeNormal;
	opt.innerShadow.colour = Colours::black;
	opt.innerShadow.opacity = 0.5;
	opt.innerShadow.angle = vf::degreesToRadians <double> (90);
	opt.innerShadow.distance = 4;
	opt.innerShadow.choke = 0.0;
	opt.innerShadow.size = 8;
}
//=============================================================================
static void setButtonBaseOptions (LayerGraphics::Options &opt,
								  const int width, const int height)
{
	// General
	opt.general.opacity = 1.0;
	opt.general.groupInteriorEffects = true;
	opt.fill.opacity = 1.0;
	opt.fill.mode = BlendMode::modeNormal;
	
	// Drop Shadow
	opt.dropShadow.active = true;
	opt.dropShadow.mode = BlendMode::modeMultiply;
	opt.dropShadow.colour = Colours::black;
	opt.dropShadow.opacity = 0.5;
	opt.dropShadow.angle = vf::degreesToRadians <double> (90);
	opt.dropShadow.distance = 4;
	opt.dropShadow.spread = 0.0;
	opt.dropShadow.size = 8;

	// Inner Shadow
	opt.innerShadow.active = true;
	opt.innerShadow.mode = BlendMode::modeOverlay;
	opt.innerShadow.colour = Colours::white;
	opt.innerShadow.opacity = 0.25;
	opt.innerShadow.angle = vf::degreesToRadians <double> (90);
	opt.innerShadow.distance = 4; 
	opt.innerShadow.choke = 0.0;
	opt.innerShadow.size = 1;

	// Gradient Overlay
	/*
	opt.gradientOverlay.active = true;
	opt.gradientOverlay.mode = vf::BlendMode::modeOverlay;
	opt.gradientOverlay.opacity = 0.25;
	opt.gradientOverlay.colours = GradientColours (Colours::black, Colours::white);
	opt.gradientOverlay.reverse = false;
	opt.gradientOverlay.kind = GradientOverlayStyle::kindLinear;
	opt.gradientOverlay.angle = degreesToRadians <double> (90);
	opt.gradientOverlay.scale = 1.0;
	opt.gradientOverlay.startPoint = Point <int> (0, 0);
	opt.gradientOverlay.endPoint = Point <int> (0, height);
	*/
}
//=============================================================================
} // namespace vf
//=============================================================================
#endif  // __BUTTERSCOTCHUI_H_83A08092__
//=============================================================================

And this is the 3 parts that compose the button without text (background, base, gradient), where the gradient need to be a gradient in the base LayersOptions.

#include "vf_unfinished.h"
#include "../Font/FontManager.h"
#include "../ButterScotchUI.h"
// =============================================================================
static void drawBackground (Graphics &g,
							juce::Rectangle<int> bounds,
							juce::Rectangle<float> rect)
{
	vf::LayerGraphics depth (g, bounds);
	vf::setButtonDepthOptions (depth.getOptions ());
	depth.setColour (Colour(17,17,17)); // aka #111111
	depth.fillRoundedRectangle (rect, (float)(rect.getHeight() / 4.f));
}
// =============================================================================
static void drawBase (Graphics &g,
					  juce::Rectangle<int> bounds,
					  juce::Rectangle<float> rect)
{
	vf::LayerGraphics base (g, bounds);
	vf::setButtonBaseOptions (base.getOptions(), rect.getWidth(), rect.getHeight());
	base.setColour (Colour(63,57,51)); // aka #3f3933
	base.fillRoundedRectangle (rect, (float)(rect.getHeight() / 6.f));
}
// =============================================================================
static void drawGradient (Graphics &g,
						  juce::Rectangle<int> bounds,
						  juce::Rectangle<float> rect)
{
	vf::LayerGraphics linear (g, bounds);
	vf::LayerGraphics::Options &opt = linear.getOptions();
	opt.fill.mode = vf::BlendMode::modeOverlay;
	opt.fill.opacity = 1;
	opt.general.groupInteriorEffects = true;
	opt.general.opacity = 0.25;
	linear.setGradientFill (ColourGradient(Colours::white, 
										   rect.getX (), 
										   rect.getY (),
										   Colours::black, 
										   rect.getX (), 
										   rect.getBottom (),
										   false));
	linear.fillRoundedRectangle (rect, (float)(rect.getHeight() / 6.f));
}

The text is drawn with vf::FreeType with a embedded version of the Droid Sans typeface and a slight dropshadow with LayerEffects.

In all case, many many thanks for that project. A luxe for L’n’F and hope that you will find time to finish this work.

I see LayerEffect hasn't been updated in a few years, and I cannot seem to compile this project with JUCE3 or JUCE4 builds (or even the JUCE2 modules that are part of the source code). Has anyone been using this recently (past few months) with either JUCE3 or JUCE4? 

Just curious. 

Cheers,
B

1 Like

I also am curious — is this just permanently dead now?

It would be an amazing addition to JUCE if this library worked with JUCE 5.

I loved this lib, would love to see it working on JUCE 5. :hugs:

Nice work!
One suggestion though, instead of adding another bool parameter, that is mutually exclusive with the other one (what happens, if both are true?) I would suggest an

enum GradientType
{
    Linear, Radial, Conical
};

Good luck, would love to see that added!

3 Likes

I agree that would be a much nicer way of choosing the gradient type and I did consider adding it but thought it would break existing code since the boolean argument would no longer be needed.

In hindsight, I could simply add a new constructor that takes the GradientType as an argument instead of the boolean, but leave the other constructors for backward compatibility!

1 Like

You can create the version with the enum as argument and provide the bool version for backward compatibility, that can only select linear and radial


The compiler should allow that, since the enum is not implicitly casted to bool. Otherwise it would be a problem.

And it could be marked deprecated right away :wink:

BTW, you can still push to your PR so it will be updated


nice work! luckily it will be ignored by the juce team first, completely rewritten and then merged, hopefully before 2025.