我的WPF应用程序有一个自定义控件,专门用于显示位图的一部分。该位图包含在从我们公司后端Windows服务传输到我的程序的对象中,以byte
数组的形式传输。数据中包括一个矩形,指定需要显示的感兴趣的图像部分。
此外,当用户单击控件时,它在三个不同的“放大”状态之间循环。此设置称为“缩放状态”。在一种设置下,显示的矩形宽度比通过数据传输的矩形要大60个像素。第二种设置下,矩形的宽度增加25%,第三种设置下,宽度增加50%。高度始终计算为保持位图部分和控件相同的纵横比。
迄今为止,我一直在生成新的经过裁剪的位图来显示上述计算出的矩形区域。然而,考虑到我们接收的数据量和位图的大小,这占用了大量的内存。我需要找到一种减少内存消耗的方法。
我在WPF中搜索裁剪图像并找到了此MSDN文章,介绍如何裁剪图像。因为我已经计算了矩形,它似乎非常理想,而且看起来会使用更少的内存。因此,今天早上我修改了我的代码,不是从原始图像和Int32Rect
创建一个CroppedBitmap
,而是创建了一个RetangleGeometry
结构体,并将Image
控件的Clip
属性设置为该矩形。然而,结果是我根本就没有看到任何东西。
我注释掉了创建RectangleGeometry
的代码,此时可以看到控件中的整个图像,因此我知道问题出在计算矩形的代码中。我知道代码中的计算是正确的,但在将其转换为RectangleGeometry
时,我必须遗漏了某些内容。
下面是自定义控件使用的模板:
<Style TargetType="{x:Type local:ZoomableImage}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ZoomableImage}">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
ContextMenu="{TemplateBinding ContextMenu}"
Cursor="{TemplateBinding Cursor}"
Margin="{TemplateBinding Margin}"
Name="ImageBorder">
<Image BitmapEffect="{TemplateBinding BitmapEffect}"
BitmapEffectInput="{TemplateBinding BitmapEffectInput}"
CacheMode="{TemplateBinding CacheMode}"
Effect="{TemplateBinding Effect}"
HorizontalAlignment="Stretch"
Name="Image"
Source="{TemplateBinding ImageToBeZoomed}"
Stretch="{TemplateBinding Stretch}"
VerticalAlignment="Stretch" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
以下是计算裁剪矩形的代码:
private void CreateClipRectangle() {
Rect srcRect = new Rect( PlateRectangle.X, PlateRectangle.Y, PlateRectangle.Width, PlateRectangle.Height );
// We want to show some pixels outside the plate's rectangle, so add 60 to the PlateRectangle's Width.
srcRect.Width += 60.0;
// Adjust the Width property for the ZoomState, which increases the height & width of the rectangle around the license plate
if ( ZoomState == ZoomStates.ZoomPlus25 ) {
srcRect.Width = srcRect.Width * 1.25;
} else if ( ZoomState == ZoomStates.ZoomPlus50 ) {
srcRect.Width = srcRect.Width * 1.50;
}
// Make sure that srcRect.Width is not bigger than the ImageToBeZoomed's PixelWidth!
if ( srcRect.Width > ImageToBeZoomed.PixelWidth ) srcRect.Width = ImageToBeZoomed.PixelWidth;
// We need to keep the aspect ratio of the source rectangle the same as the Image's.
// Compute srcRect.Height so the srcRect will have the correct aspect ratio, but don't let
// the rectangle's height get bigger than the original image's height!
srcRect.Height = Math.Min( ImageToBeZoomed.PixelHeight, Math.Round( srcRect.Width * ImageBorder.ActualHeight / ImageBorder.ActualWidth ) );
// Adjust srcRect.X & srcRect.Y to center the source image in the output image
srcRect.X = srcRect.X - ( srcRect.Width - PlateRectangle.Width ) / 2.0;
srcRect.Y = srcRect.Y - ( srcRect.Height - PlateRectangle.Height ) / 2.0;
// Adjust srcRect to keep the cropped region from going off the image's edges.
if ( srcRect.X < 0 ) srcRect.X = 0.0;
if ( srcRect.Y < 0 ) srcRect.Y = 0.0;
if ( ( srcRect.X + srcRect.Width ) > ImageToBeZoomed.PixelWidth ) srcRect.X = ImageToBeZoomed.PixelWidth - srcRect.Width;
if ( ( srcRect.Y + srcRect.Height ) > ImageToBeZoomed.PixelHeight ) srcRect.Y = ImageToBeZoomed.PixelHeight - srcRect.Height;
// Create a new RectangleGeometry object that we will use to clip the ImageToBeZoomed and put it into the Clip property.
ImageControl.Clip = new RectangleGeometry( srcRect, 0.0, 0.0 );
}
ImageToBeZoomed
是一个类型为 BitmapSource
的 DependencyProperty
。使用以下代码将 byte
数组转换为 BitmapImage
:
public static BitmapImage BitmapFromBytes( byte[] imageBytes ) {
BitmapImage image = null;
if ( imageBytes != null ) {
image = new BitmapImage();
try {
using ( MemoryStream memoryStream = new MemoryStream( imageBytes ) ) {
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = memoryStream;
image.EndInit();
// Freeze the BitmapImage. This helps plug memory leaks & saves memory.
image.Freeze();
}
} catch ( Exception ex ) {
// . . .
}
}
return image;
}
PlateRectangle
属性中的值是ints
,并且它们以像素为单位。问题是否与需要将像素转换为设备独立单位有关?
编辑1
在尝试解决此问题时,我发现如果将RectangleGeometry
结构的Rect
属性的Y坐标设置为0或负值以外的任何值,就无法看到图像。这对我来说毫无意义。在大多数情况下,关注的区域位于图像的中间,而不是顶部或底部边缘。有人有任何想法吗?
编辑2
我是唯一遇到WPF Clip
功能问题的人吗?