WPF 中Image切换ImageSource做动画

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF 中Image切换ImageSource做动画相关的知识,希望对你有一定的参考价值。

在WPF中,我想通过连续改变Image的值来做动画效果,可是当实现后发现CPU占用出去的高,经测试发是imgsourceconverter的问题。所以
请问各位人兄,是否有提高性能的方法,或者其它用C#实现高效动画播放的方法。数据源是一堆连续的图片。
感谢兄弟的回答,不过我这儿的情况可能说得不是很清楚。我下面再说说
我这儿有一堆连续的图片从网络上发过来,我每接受一张就显示,因为图片是连续的,所以会产生动画视频效果。
这个是确的。现在的问题是,我采用改变Image的属性ImageSource的方法来加载图片,CPU性能会很底。除此之外,我想用C#在WPF上直接绘图(不知道WPF里可以不)或许是一种方法,但是我不知道该怎么做,网上也没有找到适合的资料。烦请有这方面经验的朋友些能提供一些实质性的解决方案。代码或者可以实现的思路。
把一张张连续的显示在WPF用户界面上以便形成动画效果。

用连续改变image的方法来实现动画应该不是一个好主意
这就等于在一帧一帧放视频啊 性能肯定高不到哪里去的

最好是应该用矢量path来animation啊,这才是WPF原生支持的动画效果

不过考虑到你手上已经是一堆连续图片了,如果一定要一张一张播放的话,那可能就需要注意加载图片的大小和时机了。
如果所需要的大小没有那么大,就不要把原本很大的图都加载了,加载一个小的copy,还有就是事先把要用的图都加载好,不要到要播放的时候再在XAML改变Image的Source属性

XAML里Source属性是用string的,当然要用converter转换再加载对应的图片。提前加载好,后台代码直接更改Source属性……
参考技术A 在播放动画前用一个泛型的集合先把所以图片都储存起来,这样就不用转换了

public class myImage

public ImageSource AnimationImage get; private set;

public myImage(ImageSource image)

AnimationImage = image;



public class myImageCollection : List<myImage>

public void addImage(ImageSource image)

Add(new myImage(image));



在一个循环语句里每隔一个间隔就读集合,设置前台的ImageSource就可以播动画了

WPF - 将位图转换为 ImageSource

【中文标题】WPF - 将位图转换为 ImageSource【英文标题】:WPF - converting Bitmap to ImageSource 【发布时间】:2014-12-03 08:02:23 【问题描述】:

我需要将 System.Drawing.Bitmap 转换为 System.Windows.Media.ImageSource 类,以便将其绑定到 WizardPage(扩展 WPF 工具包)的 HeaderImage 控件中。 位图被设置为我编写的程序集的资源。 它被这样引用:

public Bitmap GetBitmap

   get
   
      Bitmap bitmap = new Bitmap(Resources.my_banner);
      return bitmap;
   


public ImageSource HeaderBitmap

   get
   
      ImageSourceConverter c = new ImageSourceConverter();
      return (ImageSource)c.ConvertFrom(GetBitmap);
   

转换器是我here找到的。我在

得到一个 NullReferenceException
return (ImageSource) c.ConvertFrom(Resources.my_banner);

如何初始化 ImageSource 以避免此异常?还是有其他方法? 之后我想使用它:

<xctk:WizardPage x:Name="StartPage" Height="500" Width="700"
                 HeaderImage="Binding HeaderBitmap" 
                 Enter="StartPage_OnEnter"

提前感谢您的任何回答。

【问题讨论】:

见这里:What is a NullReferenceException and how do I fix it? 【参考方案1】:
void Draw()

    System.Drawing.Bitmap bmp = new Bitmap();
    ...
    Image img = new Image();
    img.Source = BitmapToImageSource(bmp)
 


private BitmapImage BitmapToImageSource(System.Drawing.Bitmap bitmap)

    using (MemoryStream memory = new MemoryStream())
    
        bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
        memory.Position = 0;
        BitmapImage bitmapimage = new BitmapImage();
        bitmapimage.BeginInit();
        bitmapimage.StreamSource = memory;
        bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapimage.EndInit();
        return bitmapimage;
    

【讨论】:

【参考方案2】:

对其他人来说,这是可行的:

    //If you get 'dllimport unknown'-, then add 'using System.Runtime.InteropServices;'
    [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool DeleteObject([In] IntPtr hObject);

    public ImageSource ImageSourceFromBitmap(Bitmap bmp)
    
        var handle = bmp.GetHbitmap();
        try
        
            return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
        
        finally  DeleteObject(handle);                
    

【讨论】:

你是我的英雄。用它来为 Bitmap 类做一个扩展,效果很好。干得好。 @ARidder101 我确定我是从其他人那里得到的。 这种方法提供了非常低质量的透明度。 Mike Strobel 下面的回答 (***.com/a/26261562/418362) 要好得多,不需要任何互操作。 让我知道当我没有调用 DeleteObject(handle) 时会发生什么。我有一个问题。谢谢。 早在 .NET 2.0 天的 Windows XP 上,可以同时打开 10.000 个句柄的限制。当达到该限制时,机器冻结了。 - 所以不调用删除,意味着你会泄漏一个句柄。 .NET 可以在某处适当地清理它,但您很可能会受到性能影响。'【参考方案3】:

dethSwatch - 感谢您在上面的回答!它帮助很大!在实现它之后,我得到了想要的行为,但我发现我在程序的另一部分遇到了内存/句柄问题。我将代码更改如下,使其更加冗长,问题就消失了。再次感谢!

    [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool DeleteObject([In] IntPtr hObject);

    public ImageSource ImageSourceForBitmap(Bitmap bmp)
    
        var handle = bmp.GetHbitmap();
        try
        
            ImageSource newSource = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

            DeleteObject(handle);
            return newSource;
        
        catch (Exception ex)
        
            DeleteObject(handle);
            return null;
        
    

【讨论】:

你的一个更漂亮的变体:) ``` var handle = bmp.GetHbitmap();尝试 返回 Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); 捕捉 返回空值; 最后 Win32.DeleteObject(handle); ```【参考方案4】:

对我来说最简单的解决方案:

ImageBrush myBrush = new ImageBrush();
var bitmap = System.Drawing.Image.FromFile("pic1.bmp");
Bitmap bitmap = new System.Drawing.Bitmap(image);//it is in the memory now
var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(),IntPtr.Zero,Int32Rect.Empty,BitmapSizeOptions.FromEmptyOptions());
myBrush.ImageSource = bitmapSource;
cover.MainGrid.Background = myBrush;
cover.Show();
bitmap.Dispose();

【讨论】:

我认为GetHbitmap的hande应该被释放。【参考方案5】:

为了搜索者的利益,我基于detailed solution 创建了一个快速转换器。

目前没有问题。

using System;
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;

namespace XYZ.Helpers

    public class ConvertBitmapToBitmapImage
    
        /// <summary>
        /// Takes a bitmap and converts it to an image that can be handled by WPF ImageBrush
        /// </summary>
        /// <param name="src">A bitmap image</param>
        /// <returns>The image as a BitmapImage for WPF</returns>
        public BitmapImage Convert(Bitmap src)
        
            MemoryStream ms = new MemoryStream();
            ((System.Drawing.Bitmap)src).Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            ms.Seek(0, SeekOrigin.Begin);
            image.StreamSource = ms;
            image.EndInit();
            return image;
        
    

【讨论】:

今天你是我的神 我喜欢这个。我喜欢这个。【参考方案6】:

我不相信ImageSourceConverter 会从System.Drawing.Bitmap 转换。但是,您可以使用以下内容:

public static BitmapSource CreateBitmapSourceFromGdiBitmap(Bitmap bitmap)

    if (bitmap == null)
        throw new ArgumentNullException("bitmap");

    var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

    var bitmapData = bitmap.LockBits(
        rect,
        ImageLockMode.ReadWrite,
        PixelFormat.Format32bppArgb);

    try
    
        var size = (rect.Width * rect.Height) * 4;

        return BitmapSource.Create(
            bitmap.Width,
            bitmap.Height,
            bitmap.HorizontalResolution,
            bitmap.VerticalResolution,
            PixelFormats.Bgra32,
            null,
            bitmapData.Scan0,
            size,
            bitmapData.Stride);
    
    finally
    
        bitmap.UnlockBits(bitmapData);
    

此方案要求源图片为 Bgra32 格式;如果您正在处理其他格式,则可能需要添加转换。

【讨论】:

以上是关于WPF 中Image切换ImageSource做动画的主要内容,如果未能解决你的问题,请参考以下文章

wpf中怎么获取image上的图并转化为bitmap

WPF中获取image图片

WPF C# 图像源

值转换器 WPF中Image数据绑定Icon对象

WPF 添加提示动画

WPF 添加提示动画