【问题标题】:Save canvas to bitmap将画布保存到位图
【发布时间】:2021-07-08 21:00:11
【问题描述】:

我想将我的画布保存为位图。我在互联网上找到了一些例子,但所有这些都只保存黑色图像(我的画布大小)。 我可以用这个做什么?

代码:

    public static void SaveCanvasToFile(Canvas surface, string filename)
    {
        Size size = new Size(surface.Width, surface.Height);

        surface.Measure(size);
        surface.Arrange(new Rect(size));

        // Create a render bitmap and push the surface to it
        RenderTargetBitmap renderBitmap =
          new RenderTargetBitmap(
            (int)size.Width,
            (int)size.Height,
            96d,
            96d,
            PixelFormats.Pbgra32);
        renderBitmap.Render(surface);

        // Create a file stream for saving image
        using (FileStream outStream = new FileStream(filename, FileMode.Create))
        {               
            BmpBitmapEncoder encoder = new BmpBitmapEncoder();
            // push the rendered bitmap to it
            encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
            // save the data to the stream
            encoder.Save(outStream);
        }}

【问题讨论】:

  • 您是否使用调试器逐步完成了此操作?有没有什么可疑的地方,比如surface.Width 返回 0?
  • 不,在调试器中,一切正常
  • 使用您的代码,我刚刚成功渲染了一大堆 (~25) 控件(我使用了 ActualWidth 和 ActualHeight 属性,因为我很少明确设置大小)
  • 你有没有解决这个问题?我遇到了基本相同的问题...

标签: c# wpf


【解决方案1】:

试试这个答案:

public void ExportToPng(Uri path, Canvas surface)
{
  if (path == null) return;

  // Save current canvas transform
  Transform transform = surface.LayoutTransform;
  // reset current transform (in case it is scaled or rotated)
  surface.LayoutTransform = null;

  // Get the size of canvas
  Size size = new Size(surface.Width, surface.Height);
  // Measure and arrange the surface
  // VERY IMPORTANT
  surface.Measure(size);
  surface.Arrange(new Rect(size));

  // Create a render bitmap and push the surface to it
  RenderTargetBitmap renderBitmap = 
    new RenderTargetBitmap(
      (int)size.Width, 
      (int)size.Height, 
      96d, 
      96d, 
      PixelFormats.Pbgra32);
  renderBitmap.Render(surface);

  // Create a file stream for saving image
  using (FileStream outStream = new FileStream(path.LocalPath, FileMode.Create))
  {
    // Use png encoder for our data
    PngBitmapEncoder encoder = new PngBitmapEncoder();
    // push the rendered bitmap to it
    encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
    // save the data to the stream
    encoder.Save(outStream);
  }

  // Restore previously saved layout
  surface.LayoutTransform = transform;
}

为了方便起见,从[此页面]复制了此答案。http://denisvuyka.wordpress.com/2007/12/03/wpf-diagramming-saving-you-canvas-to-image-xps-document-or-raw-xaml/)

【讨论】:

  • 这对我来说很好用。实际上我使用这段代码创建了数百张图像,但有时这段代码创建的一些图像是黑色的。可能是什么问题?
【解决方案2】:

在我的渲染代码中,我在target.Arrange(new Rect(size)); 之后调用target.UpdateLayout();,也许这会解决它。另请注意,如果未设置画布背景,它将被渲染为透明,而编码为 BMP 可能会变成纯黑色,因此如果您只有黑色对象,它们可能是不可见的。

【讨论】:

  • 如果我编码为 PNG 我得到透明图像,但画布背景设置为白色
  • 画布上是否应用了任何变换?
  • 我认为您的画布有问题(请参阅我对问题的评论)
  • 真的,我在其他画布上尝试了这段代码(宽度、高度 = 自动),它工作正常。但我不明白为什么问题画布无法渲染。
  • 顺便说一句,如果控件从未绘制过(放置在 TabControl 中的其他选项卡中),我如何使用 Auto Width 和 Height 获取控件的 ActualW 和 ActualH
【解决方案3】:
var fileName = "img.jpg";
var bitMap = new WriteableBitmap(DrawCanvas, null);
var ms = new MemoryStream();
System.Windows.Media.Imaging.Extensions.SaveJpeg(bitMap, ms, bitMap.PixelWidth, bitMap.PixelHeight, 0, 100);
ms.Seek(0, SeekOrigin.Begin);
var library = new MediaLibrary();
library.SavePicture(string.Format("{0}", fileName), ms);

【讨论】:

    【解决方案4】:

    注意

    如果您的渲染是黑色图像,那是因为您的尺寸不正确。

    这是一个很好的例子:

    RenderTargetBitmap rtb = new RenderTargetBitmap(width, height, mXdpi, mYdpi, System.Windows.Media.PixelFormats.Default);
            rtb.Render(my_canvas);
    
            BitmapEncoder pngEncoder = new PngBitmapEncoder();
            pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
    
            using (var fs = System.IO.File.OpenWrite("test.png"))
            {
                pngEncoder.Save(fs);
            }
    

    此代码保存从画布渲染的位图中的 png 图像。

    希望能帮到你。

    【讨论】:

      【解决方案5】:

      如果您使用的是 Windows 10 UWP 应用,请查看此页面:

      RenderTargetBitmap Class on Microsoft Docs

      作为奖励,UWP 简化了流程。

      【讨论】:

      【解决方案6】:
      using System.ComponentModel;
      using System.Diagnostics;
      using System.IO;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Media;
      using System.Windows.Media.Animation;
      using System.Windows.Media.Effects;
      using System.Windows.Media.Imaging;
      using System.Windows.Shapes;
      public static class RenderVisualService
      {
          public const double defaultDpi = 96.0;
          public static void SaveAsPng(this FrameworkElement frElement, string fullFilePath, FormattedText title = null, Point pt = default(Point))
          {
              BitmapImage img = RenderVisualService.RenderToPNGImageSource(frElement, title, pt) as BitmapImage;
              img.SavePng(fullFilePath);
          }
          public static ImageSource RenderToPNGImageSource(Visual targetControl, FormattedText Title = null, Point pt = default(Point))
          {
              var renderTargetBitmap = targetControl.GetRenderTargetBitmapFromControl(Title);
              var encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
              encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(renderTargetBitmap));
              var result = new System.Windows.Media.Imaging.BitmapImage();
              using (var memoryStream = new System.IO.MemoryStream())
              {
                  encoder.Save(memoryStream);
                  memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
      
                  result.BeginInit();
                  result.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad;
                  result.StreamSource = memoryStream;
                  result.EndInit();
              }
              return result;
          }
          public static RenderTargetBitmap ExportToPng(this Shape shape, Uri path)
          {
              // Save current canvas transform
              Transform transform = shape.LayoutTransform;
              // reset current transform (in case it is scaled or rotated)
              shape.LayoutTransform = null;
      
              // Get the size of canvas
              Size size;
              //if (!sizeOfShape.HasValue) size = new Size(shape.Width, shape.Height);
              //else size = sizeOfShape.Value;
              // Measure and arrange the surface
              // VERY IMPORTANT
              shape.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
              size = shape.DesiredSize;
              shape.Arrange(new Rect(size));
              // Create a render bitmap and push the surface to it
              RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96d, 96d, PixelFormats.Pbgra32);
              renderBitmap.Render(shape);
              if (path == null) return renderBitmap;
              // Create a file stream for saving image
              using (FileStream outStream = new FileStream(path.LocalPath, FileMode.Create))
              {
                  // Use png encoder for our data
                  PngBitmapEncoder encoder = new PngBitmapEncoder();
                  // push the rendered bitmap to it
                  encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
                  // save the data to the stream
                  encoder.Save(outStream);
              }
      
              // Restore previously saved layout
              shape.LayoutTransform = transform;
              return renderBitmap;
          }
          public static RenderTargetBitmap ExportToPng(this Canvas surface, String path, Size? sizeOfCanvas = null)
          {
              // Save current canvas transform
              Transform transform = surface.LayoutTransform;
              // reset current transform (in case it is scaled or rotated)
              var OldTransform = surface.LayoutTransform;
              surface.LayoutTransform = null;
              try
              {
                  // Get the size of canvas
                  Size size;
                  if (!sizeOfCanvas.HasValue) size = new Size(surface.Width, surface.Height);
                  else size = sizeOfCanvas.Value;
                  //Size sizeR = new Size(300, 300);
                  // Measure and arrange the surface
                  // VERY IMPORTANT
                  surface.Measure(size);
                  surface.Arrange(new Rect(size));
      
                  // Create a render bitmap and push the surface to it
                  RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96d, 96d, PixelFormats.Pbgra32);
                  renderBitmap.Render(surface);
                  if (path == null) return renderBitmap;
                  // Create a file stream for saving image
                  using (FileStream outStream = new FileStream(path, FileMode.Create))
                  {
                      // Use png encoder for our data
                      PngBitmapEncoder encoder = new PngBitmapEncoder();
                      // push the rendered bitmap to it
                      encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
                      // save the data to the stream
                      encoder.Save(outStream);
                  }
                  // Restore previously saved layout
                  surface.LayoutTransform = transform;
                  return renderBitmap;
              }
              catch { return null; }
              finally { surface.LayoutTransform = OldTransform; }
          }
          public static void SaveBitmapSourceToPNGFile(System.Windows.Media.Imaging.BitmapSource renderTargetBitmap, string filename)
          {
              var encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
              encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(renderTargetBitmap));
              var result = new System.Windows.Media.Imaging.BitmapImage();
              try { using (var fileStream = new System.IO.FileStream(filename, System.IO.FileMode.Create)) { encoder.Save(fileStream); } }
              catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"There was an error saving the file: {ex.ProcessException()}"); }
          }
          public static System.Windows.Media.Imaging.BitmapSource GetRenderTargetBitmapFromControl(this Visual targetControl, FormattedText Title, Point ptTitlePosition = default(Point), double dpi = defaultDpi)
          {
              if (targetControl == null) return null;
              var bounds = VisualTreeHelper.GetDescendantBounds(targetControl);
              //int pixelX = (int)bounds.Width; int pixelY = (int)bounds.Height;
              //IntPtr hDc = Helper.GetDC(IntPtr.Zero);
              //if (hDc != IntPtr.Zero)
              //{
              //    int dpiX = Helper.GetDeviceCaps(hDc, Helper.LOGPIXELSX);
              //    int dpiY = Helper.GetDeviceCaps(hDc, Helper.LOGPIXELSY);
              //    Helper.ReleaseDC(IntPtr.Zero, hDc);
              //    pixelX = (int)(((double)dpiX / 96) * (double)bounds.Width);
              //    pixelY = (int)(((double)dpiY / 96) * (double)bounds.Height);
              //}
              var renderTargetBitmap = new System.Windows.Media.Imaging.RenderTargetBitmap(/*(int)(bounds.Width * dpi / 96.0),(int)(bounds.Height * dpi / 96.0),*/(int)bounds.Size.Width, (int)bounds.Size.Height, dpi, dpi, PixelFormats.Pbgra32);
              var drawingVisual = new DrawingVisual();
              using (DrawingContext dc = drawingVisual.RenderOpen())
              {
                  var visualBrush = new VisualBrush(targetControl);
                  dc.DrawRectangle(visualBrush, null, new Rect(new Point(), bounds.Size));
                  if (null != Title) dc.DrawText(Title, ptTitlePosition);
              }
              renderTargetBitmap.Render(drawingVisual);
              //Clipboard.SetImage(renderTargetBitmap);
              return renderTargetBitmap;
          }
          public static Size GetDimensionsFromWindowToPixel(this ScrollViewer scroller)
          {
              IntPtr hDc = Helper.GetDC(IntPtr.Zero);
              double pixelX = -10, pixelY = -10;
              if (hDc != IntPtr.Zero)
              {
                  int dpiX = Helper.GetDeviceCaps(hDc, Helper.LOGPIXELSX);
                  int dpiY = Helper.GetDeviceCaps(hDc, Helper.LOGPIXELSY);
      
                  Helper.ReleaseDC(IntPtr.Zero, hDc);
      
                  pixelX = (int)(((double)dpiX / 96) * (double)(scroller.ActualWidth - SystemParameters.VerticalScrollBarWidth));
                  pixelY = (int)(((double)dpiY / 96) * (double)(scroller.ActualHeight - SystemParameters.HorizontalScrollBarHeight));
              }
              Size sz = new Size(0, 0);
              if (pixelX > 0) sz.Width = pixelX; if (pixelY > 0) sz.Height = pixelY;
              return sz;
          }
      
          /// <summary>
          /// This method is almost same as RenderVisualService.SaveAsPng , this method explores the range of possibilites in one place
          /// which is also an extension Method , this one has some cool arguments</summary>
          /// <param name="fr"></param>
          /// <param name="pngIsTrueAndClipBoardisFalse"></param> <param name="pngFileFullPath"></param>
          /// <param name="x"></param> <param name="y"></param>
          /// <param name="wd"></param> <param name="ht"></param>
          /// <param name="margin"></param>
          /// <param name="dotsPerInch"></param>
          public static void DumpFrameworkElementToPng(this FrameworkElement fr, bool pngIsTrueAndClipBoardisFalse = true, String pngFileFullPath = @"C:\WOI\A\control.png", int x = 0, int y = 0, int wd = 0, int ht = 0, int margin = 20, int border = 5, double dotsPerInch = 96.0)
          {
              Directory.CreateDirectory(System.IO.Path.GetDirectoryName(pngFileFullPath));
              System.Windows.Media.PixelFormat pf = System.Windows.Media.PixelFormats.Pbgra32;
              GeneralTransform t = fr.TransformToVisual(fr.Parent as Visual);
      
              Vector topLeft = (Vector)t.Transform(new Point(0, 0));
              Vector topRight = (Vector)t.Transform(new Point(fr.ActualWidth, 0));
              Vector botmLeft = (Vector)t.Transform(new Point(0, fr.ActualHeight));
      
              double renderedWidth = (topRight - topLeft).Length; double widthScale = renderedWidth / fr.ActualWidth;
              double renderedHight = (botmLeft - topLeft).Length; double heightScale = renderedHight / fr.ActualHeight;
      
              //int fullWidth = (int)fr.ActualWidth + 2 * margin; int fullHeight = (int)fr.ActualHeight + 2 * margin;
              int fullWidth = (int)renderedWidth + (2 * margin); int fullHeight = (int)renderedHight + (2 * margin);
              var renderTargetBitmap = new RenderTargetBitmap(fullWidth, fullHeight, dotsPerInch, dotsPerInch, pf);
      
              var drawingVisual = new System.Windows.Media.DrawingVisual();
              RenderOptions.SetBitmapScalingMode(drawingVisual, BitmapScalingMode.HighQuality);
              using (System.Windows.Media.DrawingContext dc = drawingVisual.RenderOpen())
              {
                  var vb = new System.Windows.Media.VisualBrush(fr);
                  dc.DrawRectangle(Brushes.AliceBlue, null, new Rect(0, 0, fullWidth, fullHeight));
                  dc.DrawRectangle(vb, null, new Rect(margin, margin, fullWidth, fullHeight));
              }
              renderTargetBitmap.Render(drawingVisual);
              BitmapSource bmpSource = renderTargetBitmap;
              if (wd != 0 && ht != 0)
              {
                  if (wd > fr.ActualWidth || ht > fr.ActualHeight)
                  {
                      wd = fullWidth; ht = fullHeight;
                      x = y = 0;
                  }
                  else { wd -= margin; ht -= margin; }
                  bmpSource = new CroppedBitmap(renderTargetBitmap, new Int32Rect(x + margin, y + margin, wd, ht));
                  if (margin != 0)
                  {
                      var dv = new DrawingVisual();
                      using (var dc = dv.RenderOpen())
                      {
                          fullWidth = wd + 2 * margin; fullHeight = ht + 2 * margin;
                          dc.DrawRectangle(Brushes.AliceBlue, null, new Rect(0, 0, fullWidth, fullHeight));
                          dc.DrawImage(bmpSource, new Rect(margin, margin, wd, ht));
                          if (border != 0) dc.DrawRectangle(null, new Pen(Brushes.DarkBlue, border), new Rect(margin - border / 2, margin - border / 2, wd + border, ht + border));
                      }
                      renderTargetBitmap = new RenderTargetBitmap(fullWidth, fullHeight, dotsPerInch, dotsPerInch, pf);
                      renderTargetBitmap.Render(dv);
                      bmpSource = new CroppedBitmap(renderTargetBitmap, new Int32Rect(0, 0, fullWidth, fullHeight));
                  }
              }
              if (pngIsTrueAndClipBoardisFalse)
              {
                  bmpSource.SavePng(pngFileFullPath);
                  Helper.ShellExecute(IntPtr.Zero, "open", pngFileFullPath, null, null, 1);
              }
              else
              {
                  Clipboard.SetImage(bmpSource);
                  Helper.ShellExecute(IntPtr.Zero, "open", "pBrush", null, null, 1);
              }
          }
          /// <summary>
          /// Applicable to methods like MakeJulia_Sai in SpecialCanvas
          /// Usage : RenderVisualService.SaveARenderMethodToPng(Kanva.MakeJulia_Sai, Kanva.ActualWidth / 4, Kanva.ActualHeight / 4);
          /// </summary>
          /// <param name="method"></param>
          public static void SaveARenderMethodToPngOrClipBoard(Action<System.Windows.Media.DrawingContext> method, double wd, double ht, bool pngIsTrueAndClipBoardisFalse = true, String pngFileFullPath = @"C:\WOI\A\Bindu.png")
          {
              double dotsPerInch = 96.0; System.Windows.Media.PixelFormat pf = System.Windows.Media.PixelFormats.Pbgra32;
              //double wd = Kanva.ActualWidth / 4; double ht = Kanva.ActualHeight / 4;
              var renderTargetBitmap = new System.Windows.Media.Imaging.RenderTargetBitmap((int)wd, (int)ht, dotsPerInch, RenderVisualService.defaultDpi, pf);
              var drawingVisual = new System.Windows.Media.DrawingVisual();
              using (System.Windows.Media.DrawingContext dc = drawingVisual.RenderOpen()) method(dc);
              renderTargetBitmap.Render(drawingVisual);
              if (pngIsTrueAndClipBoardisFalse)
              {
                  Directory.CreateDirectory(System.IO.Path.GetDirectoryName(pngFileFullPath));
                  renderTargetBitmap.SavePng(pngFileFullPath);
                  Helper.ShellExecute(IntPtr.Zero, "open", pngFileFullPath, null, null, 1);
              }
              else Clipboard.SetImage(renderTargetBitmap);
          }
      }
      

      我最喜欢的是

      Kanva.DumpFrameworkElementToPng(pngIsTrueAndClipBoardisFalse: false)
      

      Kanva 是一些带有一些控件、图像等的 Canvas。调用 DumpFrameworkElementToPng(...) 后,图像保存在剪贴板中

      【讨论】:

        【解决方案7】:

        尝试将画布背景颜色设置为白色

        【讨论】:

          猜你喜欢
          • 2013-02-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-22
          • 1970-01-01
          • 2013-08-20
          • 2021-10-13
          • 1970-01-01
          相关资源
          最近更新 更多