iPlug2 / iPlug2

C++ Audio Plug-in Framework for desktop, mobile and web
https://iplug2.github.io
Other
1.92k stars 284 forks source link

Implement Drop Shadow for all IGraphics Backends [was workast #TZBM] #38

Closed olilarkin closed 5 years ago

olilarkin commented 5 years ago

https://github.com/olilarkin/wdl-ol-private/blob/master/IGraphics/IGraphics.h#L461

hor-net commented 5 years ago

I already have some composition logic for cairo for dropshadow but it's experimental.

Actually each IControl call a "start layer" and "end layer" in it's draw, this creates a temporary cairo surface on which the control is drawn. Then every shadow, transparency or other effect is applied to this temp surface. At the end everything is flatten to the final surface and rendered with the image backend.

It's not that fast but it's working right now if you want to have a look at it. (it's basically the same principle that photoshop uses)

Only issue, we need to find a blur function that actually we can use since to test if it works I've used the GTK one (and it's GPL)

Another thing, non of my plugin uses this code right now so it's completely experimental :D

AlexHarker commented 5 years ago

Sadly the problem is not getting it work in Cairo. I have had a Cairo drop shadow based on a hand-rolled blur function (gaussian) for quite some time that is quite stable. It works similarly to the way you describe, only using groups which is easier (and possibly faster) than managing the surfaces yourself. The problem is that iPlug supports more drawing APIs than Cairo, so having a Cairo solution doesn't solve the problem for all the other platforms. Building groups/layers onto of those drawing APIs is one possible solution, but it also depends on being able to calculate draw extents correctly.

Bottom line is that unfortunately this is non-trivial.

AlexHarker commented 5 years ago

My vector library that runs on top of IGraphics (it used to render internally, then I moved to Cairo, now I run on IGraphics with an assumption of Cairo) is here:

https://github.com/AlexHarker/HISSTools_PluginLib/tree/master/HISSTools_Graphics

If I can move all the bits that are Cairo specific into IGraphics for all drawing APIs we'd have something, but that is quite a bit to sort out.

hor-net commented 5 years ago

As far as i know it should be possible to render NanoVG paths to a temp bitmap then apply blur to that bitmap (with OpenGL) and work exactly like you would do with Cairo. Se here: https://github.com/memononen/nanovg/issues/227#issuecomment-122508083

I don't know NanoVG but i think i'll dig deeper into that and see what i can do with it

madronalabs commented 5 years ago

Do all these APIs have functions to draw a stroke with variable opacity? If so, we could draw shadows around shapes using that feature and save more involved compositing for later.

AlexHarker commented 5 years ago

@hor-net - yes it's not impossible to do temp bitmaps etc., but you only want to run the blur on the section you're drawing to (and hence draw extents are useful). Rebuilding the workflow I've used in cairo in other APIs is possible, just a little time consuming.

@madronalabs - all the ones in which we might support drop-shadow (everything except LICE) do support that, but even a solid shape that isn't at 100% opacity wouldn't draw correctly using strokes.

madronalabs commented 5 years ago

I proposed this because I use this technique in all my UIs. It may or may not be suited for more general use as in a graphics library but as a shadow generator, seems fine. I'll attach the relevant code in case it is of interest. This is for arcs (all I needed). For rectangles one would want to round off the corners and so on.

{   
    // outer shadow
    juce::Path outline;
    float d, opacity;
    for (int i=0; i<mShadowSize; i++)
    {
        outline.clear();            
        outline.addCentredArc(cx, cy, r1 + i + 0.5, r1 + i + 0.5, 0., rotaryStart, rotaryEnd, true);
        d = (float)(mShadowSize - i) / (float)mShadowSize; // 1 -- 1/n
        opacity = d * d * d * kMLShadowOpacity;
        sg.setColour (shadow.withAlpha(opacity));
        sg.strokePath (outline, PathStrokeType (1.f));  // thickness = 1
    }               
}       
AlexHarker commented 5 years ago

So this is now implemented on the graphics-drop-shadow branch and working for everything except NanoVG with MTL on OSX. Implemented as a pixel raster gaussian blur with offset and opacity control for the new layers system

olilarkin commented 5 years ago

Merged

madronalabs commented 5 years ago

Great work, I look forward to trying this. NanoVG with MTL on OSX is my main target—was there any particular problem there?

AlexHarker commented 5 years ago

Yes - at the moment we can't get the pixel data from within Metal. As soon as that is fixed the drop-shadow will work. It's a well isolated problem - it just goes outside of IPlug code. I attach a pic.

screen shot 2018-12-28 at 19 27 09

](url)

olilarkin commented 5 years ago

on ios it works because the texture is in memory shared by the cpu/gpu i think. we need to do something with this [https://developer.apple.com/documentation/metal/mtlblitcommandencoder?language=objc] to make it work on discrete gpus

olilarkin commented 5 years ago

metal on discrete GPUs fixed in d77d9a9e3f272ac0af6d564ccd49154f912c8717 yey

AlexHarker commented 5 years ago

Awesome!