AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.16k stars 2.18k forks source link

Shadow effect #2605

Closed TheSuunny closed 3 years ago

TheSuunny commented 5 years ago

Hello, unfortunately I have not found an answer to this question anywhere, and only Pull Request which has been closed, how can I create DropShadow on an item?

This is a very important part in almost any design.

To example as here

image

kekekeks commented 5 years ago

Blocked by https://github.com/AvaloniaUI/Avalonia/issues/2244 @grokys

kekekeks commented 5 years ago

Basically we need a proper layer system implementation that understands concepts of nesting, bounds and z-order.

kekekeks commented 5 years ago

We can implement simple box-shadow effect that doesn't require any layers though. Something that works like box-shadow in CSS.

kekekeks commented 5 years ago

The difference between WPF and Browsers:

WPF's DropShadowEffect:

<Border Margin="10" BorderBrush="Black" BorderThickness="1">
    <Border.Effect>
        <DropShadowEffect/>
    </Border.Effect>
    <StackPanel Orientation="Vertical" Margin="5">
        <Label Content="Staying within borders"/>
        <Button Content="Do It"/>
    </StackPanel>
</Border>

Produces a proper shadow for all elements: изображение

CSS box-shadow:

<div style="background:transparent; box-shadow: red 10px 5px 5px; padding: 5px">
    <p>Staying within borders</p>
    <button>Do it!</button>
</div>

Produces a shadow for the bounding rectangle (round corners are supported) of the div: изображение

We can implement box-shadow quite easily while WPF-like shadow would be troublesome.

Gillibald commented 5 years ago

Isn't it possible to use a RenderTargetBitmap in combination with a CustomDrawOperation to apply a shader on all drawing calls when a control is rendered to that bitmap? That is somehow like Avalonia's layering but without the scene graph.

You will lose hit testing because the scene graph is responsible for that.

kekekeks commented 5 years ago

I think that might break when layers actually come into play. Also, such shadows should be supported by DeferredRenderer itself for invalidation purposes.

kekekeks commented 5 years ago

I think we can add drop-shadow effect support for simple visuals like rectangles, paths, text and images. That would be a sufficient building block for most of the UI elements. After all, browsers only support box-shadow and text-shadow.

kekekeks commented 5 years ago
      <Path Fill="OrangeRed" Canvas.Left="180" Canvas.Top="250">
        <Path.ImageFilter>
            <DropShadowImageFilter Offset="10,20" Blur="5,15"  Color="Green"/>
        </Path.ImageFilter>  
        <Path.Data>
          <PathGeometry>
            <PathFigure StartPoint="0,0" IsClosed="True">
              <QuadraticBezierSegment Point1="50,0" Point2="50,-50" />
              <QuadraticBezierSegment Point1="100,-50" Point2="100,0" />
              <LineSegment Point="50,0" />
              <LineSegment Point="50,50" />
            </PathFigure>
          </PathGeometry>
        </Path.Data>
      </Path>

изображение

Gillibald commented 5 years ago

How do you add this to the DrawingContext? Is it possible to add PushImageFilter(Image Filter filter) and PopImageFilter() that way it is possible to add these in custom scenarios for specific drawing operations.

kekekeks commented 5 years ago

Push/Pop won't properly without using layers at least internally, also, combining them would be troublesome. As I've suggested, for now I'm adding image filters for primitive drawing operations

Gillibald commented 5 years ago

Direct2D effects only work on I2D1Bitmap so you always need a surface to draw on before you can apply an effect. If we were wrapping drawing calls with a RenderTargetBitmap we could easily apply effects in the way Direct2D works. This kind of works like a layer I guess. The render pipeline needs to redirect all drawing calls to that wrapping RenderTargetBitmap and after the root node is rendered the effect is applied and the result is transferred to the main surface. So without effect layers this will not be supported by Direct2D. Skia could easily adapt this approach.

kekekeks commented 5 years ago

We are considering to drop Direct2D completely

kekekeks commented 5 years ago

image-filter

          <Style Selector="Image">
              <Style.Animations>
                  <Animation Duration="0:0:2"
                             IterationCount="Infinite"
                             FillMode="None"
                             PlaybackDirection="AlternateReverse"
                             Easing="SineEaseInOut">
                      <KeyFrame Cue="0%">
                          <Setter Property="DropShadowImageFilter.Offset" Value="0,0"/>
                          <Setter Property="DropShadowImageFilter.Blur" Value="5,5"/>
                          <Setter Property="DropShadowImageFilter.Color" Value="Green"/>
                      </KeyFrame>
                      <KeyFrame Cue="100%">
                          <Setter Property="DropShadowImageFilter.Offset" Value="40,60"/>
                          <Setter Property="DropShadowImageFilter.Blur" Value="20,30"/>
                          <Setter Property="DropShadowImageFilter.Color" Value="Blue"/>
                      </KeyFrame>
                  </Animation>
              </Style.Animations>
          </Style>
maxkatz6 commented 3 years ago

https://github.com/AvaloniaUI/Avalonia/pull/3871

legistek commented 1 year ago

Is BoxShadow still not supposed to work when using Direct2D rendering?

kekekeks commented 1 year ago

Effects are currently only supported for Skia backend.

legistek commented 1 year ago

Got it thanks. Is there a list anywhere of all the limitations of the D2D renderer vs. Skia? Much appreciated.