【问题标题】:Convert Canvas to BitMapImage & crop to path将 Canvas 转换为位图图像并裁剪为路径
【发布时间】:2013-09-30 15:28:18
【问题描述】:

我在 windows phone 8 应用程序中有一个用户控件,用户在此用户控件上绘图。我想将其转换为图像,例如位图。

我找到了这个“converting a canvas into bitmap image in android”,但我需要它用于 Windows Phone 8。

用户控件位于画布中。如果我只将带有信息的用户控件转换为图像,则最佳方案是。但如果这不能做到,那么画布。如果它必须是画布,是否可以将用户控件周围的背景设置为不可见,因为不需要此信息。

编辑 也许这个链接可以帮助某人。 How to render a WPF UserControl to a bitmap without creating a window

完成后我将发布我的解决方案,我还将研究将位图转换回用户控件,如果有人对此进行了研究,请告知我。

编辑 2

有人用过这个库吗 http://writeablebitmapex.codeplex.com/ 应该很轻,可以看到有裁剪图像的功能。所以也许这正是我所需要的。

编辑 3 所以我一直在研究这个问题,终于找到了我想要的东西,见http://www.kunal-chowdhury.com/2012/12/how-to-crop-image-based-on-shape-or-path.html

但我似乎无法正常工作。有人对此有想法吗?

解决方案 我使用 writeablebitmap 来捕获 ui 元素,并使用媒体流将其保存到隔离存储中。然后我可以重新加载它并将图像用作图像源,从而将元素裁剪为所需的形状。

【问题讨论】:

  • 可能会在 WriteableBitmap 中捕获用户控件的内容。从那里,您可以使用内置 API 直接将其保存为 JPEG,或使用 ImageTools 库将其保存为其他格式。
  • 所以我必须为用户控件设置一个属性才能使其工作或?

标签: c# wpf canvas user-controls windows-phone-8


【解决方案1】:

这允许您使用 RendeTargetBitmapDrawingVisualVisualBrush 使用纯 WPF 代码来完成此操作。

Get System.Drawing.Bitmap of a WPF Area using VisualBrush

作为答案发布是因为我不得不使用视觉画笔而不是窗口来寻找解决方案。

这是重要的代码,被盗了:

public BitmapSource ConvertToBitmapSource(UIElement element)
{
    var target = new RenderTargetBitmap((int)(element.RenderSize.Width), (int)(element.RenderSize.Height), 96, 96, PixelFormats.Pbgra32);
    var brush = new VisualBrush(element);

    var visual = new DrawingVisual();
    var drawingContext = visual.RenderOpen();


    drawingContext.DrawRectangle(brush, null, new Rect(new Point(0, 0),
        new Point(element.RenderSize.Width, element.RenderSize.Height)));

    drawingContext.Close();

    target.Render(visual);

    return target;
}   

如果您想在此处添加一些额外的遮罩,请在绘制时按下不透明遮罩。

drawingContext.PushOpacityMask(brush);

但是,您不必这样做,因为可以通过 XAML 使用目标元素上的 opacity mask 属性来实现相同的效果。

这是我的示例 XAML

<Window x:Class="UIToBitmap.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Ellipse x:Name="circle" Height="100" Width="100" Fill="Blue"/>
        <Grid Background="Black">
            <Image x:Name="Image" Height="100"></Image>
        </Grid>
    </StackPanel>
</Window>

以及背后的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace UIToBitmap
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.Loaded += MainWindow_Loaded;
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            var source = ConvertToBitmapSource(circle);
            Image.Source = source;
        }

        public BitmapSource ConvertToBitmapSource(UIElement element)
        {
            var target = new RenderTargetBitmap((int)(element.RenderSize.Width), (int)(element.RenderSize.Height), 96, 96, PixelFormats.Pbgra32);
            var brush = new VisualBrush(element);

            var visual = new DrawingVisual();
            var drawingContext = visual.RenderOpen();


            drawingContext.DrawRectangle(brush, null, new Rect(new Point(0, 0),
                new Point(element.RenderSize.Width, element.RenderSize.Height)));

            drawingContext.PushOpacityMask(brush);

            drawingContext.Close();

            target.Render(visual);

            return target;
        } 
    }
}

请注意,在黑色网格上绘制的圆圈保持其不透明度 - 仅绘制圆圈。

【讨论】:

  • 我的问题是,您转换整个空间,您是否调查过您对正方形而不是三角形感兴趣。那么对于剩余区域,您希望它是透明/不可见的吗?
  • @JTIM 问题是在不打开窗口的情况下将 UI 元素(画布)转换为图像。这成功了。我将编辑一些额外内容。
【解决方案2】:
public static class SBA
{       
    public static WriteableBitmap SaveAsWriteableBitmap(Canvas surface)
    {
        if (surface == null) return null;

        // 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.ActualWidth, surface.ActualHeight);
        // Measure and arrange the surface
        // VERY IMPORTANT
        surface.Measure(size);
        surface.Arrange(new Rect(size));

        // Get the size of canvas
        size = new Size(surface.ActualWidth, surface.ActualHeight);
        // 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);

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

        //create and return a new WriteableBitmap using the RenderTargetBitmap
        return new WriteableBitmap(renderBitmap);
    }

    public static BitmapImage WriteableBitmapToBitmapImage(WriteableBitmap wbm)
    {
        BitmapImage bmImage = new BitmapImage();
        using (MemoryStream stream = new MemoryStream())
        {
            PngBitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(wbm));
            encoder.Save(stream);
            bmImage.BeginInit();
            bmImage.CacheOption = BitmapCacheOption.OnLoad;
            bmImage.StreamSource = stream;
            bmImage.EndInit();
            bmImage.Freeze();
        }
        return bmImage;
    }

    public static BitmapImage CanvasToBitmap(Canvas c)
    {
        return WriteableBitmapToBitmapImage(SaveAsWriteableBitmap(c));
    }

}

【讨论】:

    【解决方案3】:

    根据您的需要,有一个名为 WriteableBitmapEx 的第三方库,它具有您将来可能需要的许多其他功能。所以只需从 NugetPackageManager 安装此库并然后您可以在 UI 上制作任何控件的 writeableBitmap,然后将其转换为任何图像。我有一个示例,其中我将按钮转换为可写位图,然后将其保存到手机中的媒体库(照片)。这是简单的代码,但请确保您已安装 WriteableBitmapEx。

    这里是一个在 xaml 中定义的 Button..

    private void btn_Click_1(object sender, RoutedEventArgs e)
        {
    
            BitmapImage img = new BitmapImage();
    
            imagebitmap = new WriteableBitmap(btn, null);
    
            imagebitmap.SaveToMediaLibrary("hello", false);
    
        }
    

    您必须直接使用此代码。希望对你有帮助

    【讨论】:

    • 谢谢你,是的,每一个帮助都是值得的。我去图书馆看看。但是你有没有把非矩形元素做成图片的经验呢?如果是这样,控件未填充的区域会发生什么情况?
    • JTIM 我已经检查了它,将它应用在一个带有圆形边框的按钮上,实际上它只转换控制占用的区域,如果你认为你可以制作一个带有三角形尺寸的图像,那么它不是可能的,因为图像总是矩形的(我想是的)..所以只要探索它是否对你没有帮助..如果有什么请更新这里
    • 我当然会,我将通过更新我的应用程序来探索这个领域。所以想要一些关于最佳实践的信息。现在我有了一个开始的地方。只是想知道你是否有这方面的经验:)
    • 所以现在我终于有了一个需要它的应用程序,我正在尝试按照您所说的进行演示。因为我可以通过阅读 WriteableBitmapEx 的文档收集到什么,所以它正是我需要的。但我似乎无法让代码工作。我已经通过 nuget 安装了它,但是 imagebitmap 给出了一个错误。此外,您现在有办法将图片保存在独立存储中而不是应用图片中吗?
    【解决方案4】:

    解决方案我使用 writeablebitmap 来捕获 ui 元素,并使用媒体流将其保存到隔离存储中。然后我可以重新加载它并将图像用作图像源,从而将元素裁剪为所需的形状。

    【讨论】:

      猜你喜欢
      • 2013-12-20
      • 1970-01-01
      • 2018-10-23
      • 2015-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多