C# 在DrawingContext上绘制时在图像上绘制阴影
我在自定义FrameworkElement的OnRender方法中绘制图像。我想画一个阴影,这个形象以及。我需要在代码中这样做,我不想使用DropShadowBitmapEffect,因为它已经过时了。我怎样才能做到这一点C# 在DrawingContext上绘制时在图像上绘制阴影,c#,.net,wpf,C#,.net,Wpf,我在自定义FrameworkElement的OnRender方法中绘制图像。我想画一个阴影,这个形象以及。我需要在代码中这样做,我不想使用DropShadowBitmapEffect,因为它已经过时了。我怎样才能做到这一点 public class MyDrawingView : FrameworkElement { protected override void OnRender(System.Windows.Media.DrawingContext dc)
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函数创建位图,以导出带有阴影的绘制图像。将以下代码添加到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;
在
DrawingContext
上有一个旧的PushEffect()
调用,它可以完成您所需的操作,但与BitmapEffect一样,它已经过时
bitmappeffect
的替代品是Effect
类。有一个名为DropShadowEffect
的子类正是您所追求的,但不幸的是,正如SvenG所说,这不能直接应用于位图
支持应用效果的最低级别元素是DrawingVisual
类。这并不太糟糕,因为DrawingVisual
是一个非常轻量级的类。没有布局开销。最好是在自己的DrawingVisual
中创建每个位图,并将visual的Effect
属性设置为DropShadowEffect
。显然,如果你有成千上万的位图,它可能不是一个可行的解决方案
所有这些都可以在代码中完成,但不是OnRender()
,因为每个视觉对象都有自己的渲染上下文。但是,要正确渲染子绘图视觉效果,您需要向框架介绍它们
您需要在自定义元素中重写两个方法以查看这些视觉效果: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);
}
}
公共类MyDrawingView:FrameworkElement
{
列表_visuals=新列表();
公共MyDrawingView()
{
CreateVisuals();
}
//获取视觉对象及其子对象的位图呈现,以另存为图像文件
公共RenderTargetBitmap GetBitmap()
{
var rtb=新的RenderTargetBitmap(200、200、96、96,PixelFormats.Pbgra32);
rtb.渲染(此);
返回rtb;
}
受保护的重写int VisualChildrenCount
{
得到
{
返回_visuals.Count;
}
}
受保护的重写Visual GetVisualChild(int索引)
{
返回图像[索引];
}
私有void CreateVisuals()
{
CreateVisualOrbitMap(@“D:\mytemp\img1.jpg”,新的Rect(50,50,100,100));
CreateVisualOrbitMap(@“D:\mytemp\img2.jpg”,新的Rect(30,30100100));
}
私有void CreateVisualOrbitMap(字符串位图路径,矩形边界)
{
var bitmap=新的位图(新的Uri(位图路径));
var visual=新的DrawingVisual();
visual.Effect=新的DropShadowEffect();
使用(DrawingContext dc=visual.renderropen())
{
dc.DrawImage(位图、边界);
}
_视觉效果。添加(视觉);
AddVisualChild(视觉);
AddLogicalChild(可视);
}
}
此解决方案部分解决了我的问题。我的问题是,如果我有两个重叠的DrawImage,这个解决方案将从整体上绘制阴影。上面图像的阴影不会绘制在下面的图像上。嗨,Sven,你能建议我如何解决上面评论中描述的问题吗?Amaltas,问题是效果只能应用于UIElements,这就是我在这里所做的:你的DrawingView有一个效果,无论您使用drawingContext绘制了多少图像。从我的角度来看,使用FrameworkElement并在其上绘制多个位图图像感觉有点不对劲。为什么不创建一个用户控件或一个样式,其中包含DrawingView元素和其他图像控件以及要显示的图像?位图是否透明?ie是要对位图的各个像素进行阴影处理,还是只对每个位图进行矩形阴影处理?@GazTheDestroyer位图可以是透明的,因此每个位图的矩形阴影将不起作用。但是,现在我也不知道如何画矩形阴影!您建议我如何从该图形创建位图,并将其另存为图像文件?我使用原始代码中显示的getBitmap函数来执行此操作。我在上面添加了一个getBitmap()函数。这确实有效!我的一个问题是,尽管我的应用程序不会有数千个位图,但它可以有数百个由用户照片创建的位图。当用户在屏幕上绘制时,我可以使用重新调整大小的位图来节省内存,但在导出最终图像时,我希望使用完整的位图;因此,内存可能是一个问题,因为所有位图似乎都将一起加载。有没有一种方法可以按顺序加载位图?WPF渲染不是这样工作的。OnRender()有效地提供了有关如何渲染的说明,但实际渲染可以在后台由框架随时完成。因此,如果您想要全尺寸位图,即使您按顺序提供它们,WPF仍将存储它们。在您想要导出之前,您可能会使用较小的版本,但此时您仍然需要加载所有的全尺寸位图。