WPF中使用Image控件元素显示图片文件和内存Bitmap位图的方式

WPF中显示图片的控件为Image控件。以下分别介绍显示图片文件和内存位图的使用方法,主要介绍如何赋予Image对象的Source属性值。

(一)使用图片文件

方式一:

<Image Width="320"  Source="D:\\xxx.jpg"/>

方式二:

<Image Width="320">
      <Image.Source>
            <BitmapImage DecodePixelWidth="320"  UriSource="D:\\xxx.jpg" /> <!--说明:DecodePixelWidth属性告诉图片解码器解码后的位图宽度,对于大图片,不用把图片的所有数据都保存在内存中,可以省内存-->
      </Image.Source>
</Image>

方式三:
Image image = new Image();
image .Width = 200;
BitmapImage bitmapImage = new BitmapImage();


bitmapImage.BeginInit();  //给BitmapImage对象赋予数据的时候,需要用BeginInit()开始,用EndInit()结束
bitmapImage.UriSource = new Uri(@"D:\xxx.jpg");
bitmapImage.DecodePixelWidth = 320;   //对大图片,可以节省内存。尽可能不要同时设置DecodePixelWidth和DecodePixelHeight,否则宽高比可能改变
bitmapImage.EndInit();

image .Source = bitmapImage;

(二)使用内存位图(Bitmap)

有时常常不存在位图文件,而仅仅需要存在于内存中的位图,比如摄像头采集数据的每一帧,或者直接截取的屏幕位图(截屏)等等,那就不能采用以上集中方式了。WPF的Image控件的Source属性的类型为ImageSource,只要是继承自ImageSource的对象都可以赋值给Source属性。以上第一节所讲的采用的是BitmapImage,该类直接继承自BitmapSouce,间接继承自ImageSource.

为了能够将内存中的Bitmap位图显示在Image控件中,需要将Bitmap转换为ImageSource类型。通过查询与实践,总结了以下三种方式,分别为:利用不安全代码拷贝复制转换为WriteableBitmap,直接使用Imaging.CreateBitmapSourceFromHBitmap转换为BitmapSource,利用MemoryStream来创建BitmapImage。(注:个人主要比较前两种方式,第三种方式没有测试。经过测试,利用不安全代码拷贝复制的方式效率更高,对于640*480的位图,采用不安全代码拷贝的方式的时间消耗是另一种方法的25%左右,当然波动也较大)。对于确实性能要求比较高的,可以用不安全代码拷贝的方式,否则不建议用不安全代码拷贝,一方面是代码量比较多,另一方面必须要.net4.6或更高版本才可以使用Memory.Copy函数。

(1)利用不安全代码拷贝复制

                Bitmap bmp=...
                int bW=bmp.Width,bH=bmp.Height;
                WriteableBitmap writableBmp = new WriteableBitmap(640, 480, 96, 96, PixelFormats.Bgra32, null); 

              //需要注意WriteableBitmap的长和宽不要小于需要拷贝的数据大小
                int rPixelBytes = rWidth * rHeight * 4;   //字节数
      BitmapData data = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bW, bH), ImageLockMode.ReadOnly,  System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                writableBmp.Lock();
                unsafe
                    {
                        Buffer.MemoryCopy(data.Scan0.ToPointer(), writableBmp.BackBuffer.ToPointer(), rPixelBytes, rPixelBytes);

                        //Buffer.MemoryCopy需要在.net 4.6版本或更高版本上才可以使用,.net4.5不存在该方法。

                         //此外,如果不用Buffer.MemoryCopy,是否可以考虑使用Marshal中的其它方法---如Marshal.Copy,不知是否可行

                    }
                writableBmp.AddDirtyRect(new Int32Rect(0, 0, bW, bH));
                writableBmp.Unlock();
                bmp.UnlockBits(data);

(2)直接使用Imaging.CreateBitmapSourceFromHBitmap()方法创建BitmapSource

Bitmap bitmap=...

 IntPtr   myImagePtr =bitmap.GetHbitmap();     //创建GDI对象,返回指针

BitmapSouce imgsource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(myImagePtr, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());  //创建imgSource

DeleteObject(myImagePtr); 

 //一定要卸载IntPtr,否则内存泄漏很快就没有内存了。网上有人说这个方法有时不能卸载掉,我短时间测试是能够及时卸载掉内存的。需要说明的是:DeleteObject()方法不会影响imgSource和bmp中的数据,也就是DeleteObject()执行过后,imgSource和bmp中的图像数据依然完整地存在。为了能使用DeleteObject方法,需要声明来自于gdi32的外部函数 [DllImport("gdi32")]static extern int DeleteObject(IntPtr o),参考MSDN说明https://msdn.microsoft.com/zh-tw/library/1dz311e4(v=vs.80).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-4

Image.Source=imgsource.

(3)利用MemoryStream来创建BitmapImage

此种方式主要参考jumtre转载的文章,先将Bitmap数据Save到Memory Stream中,然后再用MemoryStream来赋值给BitmapImage的StreamSource属性,从而间接将Bitmap对象转换为BitmapImage对象,具体参见https://blog.csdn.net/jumtre/article/details/16800273

猜你喜欢

转载自blog.csdn.net/jiuzaizuotian2014/article/details/81279423