【问题标题】:Load a WPF BitmapImage from a System.Drawing.Bitmap从 System.Drawing.Bitmap 加载 WPF BitmapImage
【发布时间】:2010-09-10 19:15:33
【问题描述】:

我有一个 System.Drawing.Bitmap 的实例,并希望以 System.Windows.Media.Imaging.BitmapImage 的形式将其提供给我的 WPF 应用程序。

最好的方法是什么?

【问题讨论】:

    标签: c# wpf bitmap


    【解决方案1】:

    从 MemoryStream 加载它怎么样?

    using(MemoryStream memory = new MemoryStream())
    {
        bitmap.Save(memory, ImageFormat.Png);
        memory.Position = 0;
        BitmapImage bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = memory;
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.EndInit();
    }
    

    【讨论】:

    • 您可以将此代码添加为 System.Drawing.Bitmap 的扩展方法,类似于 ToBitmapImage()
    • 使用 ImageFormat.Bmp 快一个数量级。
    • 如果其他人对此代码有问题:我必须在设置 bi.StreamSource 之前添加 ms.Seek(0, SeekOrigin.Begin);。我正在使用 .NET 4.0。
    • @mls 对任何版本的 .net 都是如此。我要潜入那里修复代码;没有人告诉帕维尔。
    • 有人会考虑编辑这个答案,以便将所有(正确的)cmets 集成到其中吗?目前它的投票率很高,但根本不清楚答案或答案+cmets是“正确的”......
    【解决方案2】:

    感谢 Hallgrim,这是我最终得到的代码:

    ScreenCapture = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
       bmp.GetHbitmap(), 
       IntPtr.Zero, 
       System.Windows.Int32Rect.Empty, 
       BitmapSizeOptions.FromWidthAndHeight(width, height));
    

    我也最终绑定到了 BitmapSource 而不是 BitmapImage ,就像我原来的问题一样

    【讨论】:

    • 太棒了!为什么不选择自己的答案作为问题的答案?你的现在好多了。
    • 由于您的答案已经被接受,您可以编辑您的答案以使其更完整。
    • 请注意此代码会泄漏 HBitmap。请参阅stackoverflow.com/questions/1118496/… 进行修复
    • 警告This leaks a GDI handle 每次使用它,所以在 10k 调用后它会停止工作(如果你幸运的话,它会停止工作 65k)。如 GetHbitmap 中所述,您绝对必须在该句柄上 p/invoke DeleteObject
    • 对于最后一个参数,我使用了BitmapSizeOptions.FromEmptyOptions(),它适用于我的情况。
    【解决方案3】:

    我知道这已得到解答,但这里有几个扩展方法(用于 .NET 3.0+)进行转换。 :)

            /// <summary>
        /// Converts a <see cref="System.Drawing.Image"/> into a WPF <see cref="BitmapSource"/>.
        /// </summary>
        /// <param name="source">The source image.</param>
        /// <returns>A BitmapSource</returns>
        public static BitmapSource ToBitmapSource(this System.Drawing.Image source)
        {
            System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(source);
    
            var bitSrc = bitmap.ToBitmapSource();
    
            bitmap.Dispose();
            bitmap = null;
    
            return bitSrc;
        }
    
        /// <summary>
        /// Converts a <see cref="System.Drawing.Bitmap"/> into a WPF <see cref="BitmapSource"/>.
        /// </summary>
        /// <remarks>Uses GDI to do the conversion. Hence the call to the marshalled DeleteObject.
        /// </remarks>
        /// <param name="source">The source bitmap.</param>
        /// <returns>A BitmapSource</returns>
        public static BitmapSource ToBitmapSource(this System.Drawing.Bitmap source)
        {
            BitmapSource bitSrc = null;
    
            var hBitmap = source.GetHbitmap();
    
            try
            {
                bitSrc = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                    hBitmap,
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
            }
            catch (Win32Exception)
            {
                bitSrc = null;
            }
            finally
            {
                NativeMethods.DeleteObject(hBitmap);
            }
    
            return bitSrc;
        }
    

    和 NativeMethods 类(以安抚 FxCop)

        /// <summary>
    /// FxCop requires all Marshalled functions to be in a class called NativeMethods.
    /// </summary>
    internal static class NativeMethods
    {
        [DllImport("gdi32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool DeleteObject(IntPtr hObject);
    }
    

    【讨论】:

    【解决方案4】:

    我花了一些时间才让转换双向工作,所以这是我想出的两种扩展方法:

    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Windows.Media.Imaging;
    
    public static class BitmapConversion {
    
        public static Bitmap ToWinFormsBitmap(this BitmapSource bitmapsource) {
            using (MemoryStream stream = new MemoryStream()) {
                BitmapEncoder enc = new BmpBitmapEncoder();
                enc.Frames.Add(BitmapFrame.Create(bitmapsource));
                enc.Save(stream);
    
                using (var tempBitmap = new Bitmap(stream)) {
                    // According to MSDN, one "must keep the stream open for the lifetime of the Bitmap."
                    // So we return a copy of the new bitmap, allowing us to dispose both the bitmap and the stream.
                    return new Bitmap(tempBitmap);
                }
            }
        }
    
        public static BitmapSource ToWpfBitmap(this Bitmap bitmap) {
            using (MemoryStream stream = new MemoryStream()) {
                bitmap.Save(stream, ImageFormat.Bmp);
    
                stream.Position = 0;
                BitmapImage result = new BitmapImage();
                result.BeginInit();
                // According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
                // Force the bitmap to load right now so we can dispose the stream.
                result.CacheOption = BitmapCacheOption.OnLoad;
                result.StreamSource = stream;
                result.EndInit();
                result.Freeze();
                return result;
            }
        }
    }
    

    【讨论】:

    • 我正在使用这个,但是使用 ImageFormat.Png。否则我会在图像上看到黑色背景:stackoverflow.com/questions/4067448/…
    • 不错的答案和最好的事情:没有互操作。
    • @DanielWolf:但霍斯特是对的:BMP 格式不支持透明度。应更正此答案以改用 PNG。
    • 如果你想要透明度,BmpBitmapEncoder 应该是 PngBitmapEncoder。否则你会变黑。
    • @HorstWalter, david.pfx:感谢您的 cmets,这是有道理的。我已经有几年没有使用 .NET,所以我无法快速尝试这些更改。你们谁能编辑我的代码,确保它仍然有效?
    【解决方案5】:

    最简单的事情是如果您可以直接从文件制作 WPF 位图。

    否则您将不得不使用 System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap。

    【讨论】:

      【解决方案6】:
      // at class level;
      [System.Runtime.InteropServices.DllImport("gdi32.dll")]
      public static extern bool DeleteObject(IntPtr hObject);    // https://stackoverflow.com/a/1546121/194717
      
      
      /// <summary> 
      /// Converts a <see cref="System.Drawing.Bitmap"/> into a WPF <see cref="BitmapSource"/>. 
      /// </summary> 
      /// <remarks>Uses GDI to do the conversion. Hence the call to the marshalled DeleteObject. 
      /// </remarks> 
      /// <param name="source">The source bitmap.</param> 
      /// <returns>A BitmapSource</returns> 
      public static System.Windows.Media.Imaging.BitmapSource ToBitmapSource(this System.Drawing.Bitmap source)
      {
          var hBitmap = source.GetHbitmap();
          var result = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
      
          DeleteObject(hBitmap);
      
          return result;
      }
      

      【讨论】:

      【解决方案7】:

      您可以通过编写自定义位图源在两个命名空间(媒体和绘图)之间共享像素数据。转换将立即发生,不会分配额外的内存。如果您不想显式创建位图的副本,这就是您想要的方法。

      class SharedBitmapSource : BitmapSource, IDisposable
      {
          #region Public Properties
      
          /// <summary>
          /// I made it public so u can reuse it and get the best our of both namespaces
          /// </summary>
          public Bitmap Bitmap { get; private set; }
      
          public override double DpiX { get { return Bitmap.HorizontalResolution; } }
      
          public override double DpiY { get { return Bitmap.VerticalResolution; } }
      
          public override int PixelHeight { get { return Bitmap.Height; } }
      
          public override int PixelWidth { get { return Bitmap.Width; } }
      
          public override System.Windows.Media.PixelFormat Format { get { return ConvertPixelFormat(Bitmap.PixelFormat); } }
      
          public override BitmapPalette Palette { get { return null; } }
      
          #endregion
      
          #region Constructor/Destructor
      
          public SharedBitmapSource(int width, int height,System.Drawing.Imaging.PixelFormat sourceFormat)
              :this(new Bitmap(width,height, sourceFormat) ) { }
      
          public SharedBitmapSource(Bitmap bitmap)
          {
              Bitmap = bitmap;
          }
      
          // Use C# destructor syntax for finalization code.
          ~SharedBitmapSource()
          {
              // Simply call Dispose(false).
              Dispose(false);
          }
      
          #endregion
      
          #region Overrides
      
          public override void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset)
          {
              BitmapData sourceData = Bitmap.LockBits(
              new Rectangle(sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height),
              ImageLockMode.ReadOnly,
              Bitmap.PixelFormat);
      
              var length = sourceData.Stride * sourceData.Height;
      
              if (pixels is byte[])
              {
                  var bytes = pixels as byte[];
                  Marshal.Copy(sourceData.Scan0, bytes, 0, length);
              }
      
              Bitmap.UnlockBits(sourceData);
          }
      
          protected override Freezable CreateInstanceCore()
          {
              return (Freezable)Activator.CreateInstance(GetType());
          }
      
          #endregion
      
          #region Public Methods
      
          public BitmapSource Resize(int newWidth, int newHeight)
          {
              Image newImage = new Bitmap(newWidth, newHeight);
              using (Graphics graphicsHandle = Graphics.FromImage(newImage))
              {
                  graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
                  graphicsHandle.DrawImage(Bitmap, 0, 0, newWidth, newHeight);
              }
              return new SharedBitmapSource(newImage as Bitmap);
          }
      
          public new BitmapSource Clone()
          {
              return new SharedBitmapSource(new Bitmap(Bitmap));
          }
      
          //Implement IDisposable.
          public void Dispose()
          {
              Dispose(true);
              GC.SuppressFinalize(this);
          }
      
          #endregion
      
          #region Protected/Private Methods
      
          private static System.Windows.Media.PixelFormat ConvertPixelFormat(System.Drawing.Imaging.PixelFormat sourceFormat)
          {
              switch (sourceFormat)
              {
                  case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
                      return PixelFormats.Bgr24;
      
                  case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
                      return PixelFormats.Pbgra32;
      
                  case System.Drawing.Imaging.PixelFormat.Format32bppRgb:
                      return PixelFormats.Bgr32;
      
              }
              return new System.Windows.Media.PixelFormat();
          }
      
          private bool _disposed = false;
      
          protected virtual void Dispose(bool disposing)
          {
              if (!_disposed)
              {
                  if (disposing)
                  {
                      // Free other state (managed objects).
                  }
                  // Free your own state (unmanaged objects).
                  // Set large fields to null.
                  _disposed = true;
              }
          }
      
          #endregion
      }
      

      【讨论】:

      • 你能举个例子吗?
      • 正是我要找的东西,我希望在我编译它时这能工作=D
      • 那么如果你有Properties.Resources.Image,你想把它画到画布上,需要133行代码? WPF 不行。
      • 一行可以搞定。但是,如果您想在不制作图像数据的深层副本的情况下执行此操作。这是要走的路。
      【解决方案8】:

      我在一家图像供应商工作,并为我们的图像格式编写了一个 WPF 适配器,类似于 System.Drawing.Bitmap。

      我写这个知识库是为了向我们的客户解释它:

      http://www.atalasoft.com/kb/article.aspx?id=10156

      那里有代码可以做到这一点。您需要将 AtalaImage 替换为 Bitmap 并执行我们正在执行的等效操作 - 它应该非常简单。

      【讨论】:

      【解决方案9】:

      我对此的看法基于大量资源。 https://stackoverflow.com/a/7035036https://stackoverflow.com/a/1470182/360211

      using System;
      using System.Drawing;
      using System.Runtime.ConstrainedExecution;
      using System.Runtime.InteropServices;
      using System.Security;
      using System.Windows;
      using System.Windows.Interop;
      using System.Windows.Media.Imaging;
      using Microsoft.Win32.SafeHandles;
      
      namespace WpfHelpers
      {
          public static class BitmapToBitmapSource
          {
              public static BitmapSource ToBitmapSource(this Bitmap source)
              {
                  using (var handle = new SafeHBitmapHandle(source))
                  {
                      return Imaging.CreateBitmapSourceFromHBitmap(handle.DangerousGetHandle(),
                          IntPtr.Zero, Int32Rect.Empty,
                          BitmapSizeOptions.FromEmptyOptions());
                  }
              }
      
              [DllImport("gdi32")]
              private static extern int DeleteObject(IntPtr o);
      
              private sealed class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
              {
                  [SecurityCritical]
                  public SafeHBitmapHandle(Bitmap bitmap)
                      : base(true)
                  {
                      SetHandle(bitmap.GetHbitmap());
                  }
      
                  [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
                  protected override bool ReleaseHandle()
                  {
                      return DeleteObject(handle) > 0;
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案10】:

        我来这个问题是因为我试图做同样的事情,但在我的情况下,位图来自资源/文件。我发现最好的解决方案如以下链接所述:

        http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.aspx

        // Create the image element.
        Image simpleImage = new Image();    
        simpleImage.Width = 200;
        simpleImage.Margin = new Thickness(5);
        
        // Create source.
        BitmapImage bi = new BitmapImage();
        // BitmapImage.UriSource must be in a BeginInit/EndInit block.
        bi.BeginInit();
        bi.UriSource = new Uri(@"/sampleImages/cherries_larger.jpg",UriKind.RelativeOrAbsolute);
        bi.EndInit();
        // Set the image source.
        simpleImage.Source = bi;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多