【问题标题】:Draw shadow on image while drawing on DrawingContext在 DrawingContext 上绘图时在图像上绘制阴影
【发布时间】:2012-03-26 17:18:57
【问题描述】:

我在自定义 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函数创建一个位图,以导出带有阴影的绘制图像。

【问题讨论】:

  • 位图是透明的吗?即,您要对位图的单个像素进行阴影处理,还是对每个位图只添加一个矩形阴影?
  • @GazTheDestroyer 位图可以是透明的,因此每个位图的矩形阴影将不起作用。但是,现在我也不知道怎么画一个矩形阴影!

标签: c# .net wpf


【解决方案1】:

有一个旧的PushEffect() 调用DrawingContext 可以满足您的要求,但就像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);
    }
}

【讨论】:

  • 您如何建议我使用此绘图创建位图,然后将其保存为图像文件?我使用原始代码中显示的 getBitmap 函数来执行此操作。
  • 上面已经添加了GetBitmap()函数。
  • 这确实有效!我遇到的一个问题是,即使我的应用程序没有数千个位图,但它可以有数百个由用户照片创建的位图。当用户在屏幕上绘图时,我可以使用调整大小的位图来节省内存,但在导出最终图像时,我想使用完整的位图;所以内存可能是个问题,因为似乎所有的位图都会一起加载。有没有办法可以顺序加载位图?
  • WPF 渲染不能这样工作。 OnRender() 有效地给出了如何渲染的指令,但是实际的渲染可以在任何时候由框架在幕后完成。因此,如果您想要全尺寸位图,即使您按顺序提供它们,WPF 仍将全部存储它们。在您想要导出之前,您也许可以使用较小的版本,但此时您仍然需要加载所有完整尺寸的位图。
【解决方案2】:

将以下代码添加到您的 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;

【讨论】:

  • 这个解决方案部分解决了我的问题。我遇到的问题是,如果我有两个重叠的 DrawImage,这个解决方案将考虑整体绘制阴影。上图的阴影不会在下图上绘制。
  • 嗨 SvenG,您能否建议我如何解决上述评论中描述的问题。
  • Amaltas,问题是效果只能应用于 UIElements,这就是我在这里所做的:你的 DrawingView 有一个效果,不管你用你的 drawingContext 绘制了多少图像。从我的角度来看,拥有一个 FrameworkElement 并在其上绘制多个 BitmapImages 感觉有点不对劲。为什么不创建一个 UserControl 或 Style 包含您的 DrawingView 元素和另外的带有您要显示的图像的图像控件?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-13
  • 1970-01-01
  • 2012-06-21
  • 2012-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多