在DrawingContext上绘制时在图像上绘制阴影

本文关键字:绘制 阴影 图像 DrawingContext | 更新日期: 2023-09-27 18:20:28

我在自定义FrameworkElement的OnRender方法中绘制了一个图像。我也想画一个这个图像的阴影。我需要在代码中这样做,我不想使用DropShadowBitmapEffect,因为它已经过时了。我怎样才能做到这一点?

    public class MyDrawingView : FrameworkElement
    {
        protected override void OnRender(System.Windows.Media.DrawingContext dc)
        {
             drawImagesOnDrawingContext(dc);
        }
        public RenderTargetBitmap getBitmap()
        {
            DrawingVisual dv = new DrawingVisual();
            using (DrawingContext dcMine = dv.RenderOpen())
            {
                drawImagesOnDrawingContext(dcMine);
                dcMine.Close();
            }
            RenderTargetBitmap rtb = new RenderTargetBitmap(200, 200, 96, 96, PixelFormats.Pbgra32);
            rtb.Render(dv);
            return rtb;
        }
        private void drawImagesOnDrawingContext(System.Windows.Media.DrawingContext dc)
        {
            //how to draw shadow on bi?
            BitmapImage bi = new BitmapImage(new Uri(@"D:'mytemp'img1.jpg"));
            dc.DrawImage(bi, new Rect(50, 50, 100, 100));
            //how to draw shadow on bi1
            BitmapImage bi1 = new BitmapImage(new Uri(@"D:'mytemp'img2.jpg"));
            dc.DrawImage(bi1, new Rect(30, 30, 100, 100));
        }
    }

请注意,SvenG在下面提出的为底层元素添加效果的解决方案对我来说不起作用,因为它给整个元素带来了阴影,而不是我绘制的单个图像。例如,如果我有两个重叠的DrawImage,建议的解决方案将从整体上绘制阴影。上部图像的阴影不会绘制在下部图像上。

此外,我想使用上面显示的getBitmap函数创建一个位图,以导出带有阴影的绘制图像。

在DrawingContext上绘制时在图像上绘制阴影

DrawingContext上有一个旧的PushEffect()调用,它本可以完成您需要的操作,但与BitmapEffect一样,它已经过时了。

BitmapEffect的替代品是Effect类。有一个名为DropShadowEffect的子类正是您想要的,但不幸的是,正如SvenG所说,它不能直接应用于位图。

支持应用效果的最低级别元素是DrawingVisual类。这还不错,因为DrawingVisual是一个非常轻量级的类。没有布局开销。最好是在每个位图的DrawingVisual中创建每个位图,并将视觉的Effect属性设置为DropShadowEffect。显然,如果您有数千个位图,这可能不是一个可行的解决方案。

所有这些都可以在代码中完成,但不能在OnRender()中完成,因为每个视觉都有自己的渲染上下文。但是,要使子DrawingVisuals正确渲染,您需要向框架介绍它们。

为了查看这些视觉效果,您需要覆盖自定义元素中的两个方法:VisualChildrenCount表示您有多少子级,GetVisualChild()将它们返回到系统。正因为如此,您将需要保留一组可用的视觉效果。如果您想对AddVisualChild()AddLogicalChild()进行命中测试,也可以调用它们。

public class MyDrawingView : FrameworkElement
{
    List<DrawingVisual> _visuals = new List<DrawingVisual>();
    public MyDrawingView()
    {
        CreateVisuals();
    }
    //Gets a bitmap rendering of the visual and its children for saving as image file
    public RenderTargetBitmap GetBitmap()
    {
        var rtb = new RenderTargetBitmap(200, 200, 96, 96, PixelFormats.Pbgra32);
        rtb.Render(this);
        return rtb;
    }
    protected override int VisualChildrenCount
    {
        get
        {
            return _visuals.Count;
        }
    }
    protected override Visual GetVisualChild(int index)
    {
        return _visuals[index];
    }
    private void CreateVisuals()
    {
        CreateVisualForBitmap(@"D:'mytemp'img1.jpg", new Rect(50, 50, 100, 100));
        CreateVisualForBitmap(@"D:'mytemp'img2.jpg", new Rect(30, 30, 100, 100));
    }
    private void CreateVisualForBitmap(string bitmapPath, Rect bounds)
    {
        var bitmap    = new BitmapImage(new Uri(bitmapPath));
        var visual    = new DrawingVisual();
        visual.Effect = new DropShadowEffect();
        using (DrawingContext dc = visual.RenderOpen())
        {
            dc.DrawImage(bitmap, bounds);
        }
        _visuals.Add(visual);
        AddVisualChild(visual);
        AddLogicalChild(visual);
    }
}

将以下代码添加到OnRenderMethod中:

  ....
  dc.DrawImage(bi, new Rect(50, 50, 100, 100));
  // Create DropShadow
  DropShadowEffect effect = new DropShadowEffect();
  effect = new DropShadowEffect();
  effect.Color = Colors.Gray;
  effect.Direction = 45;
  this.Effect = effect;