C# 在WPF中创建复合位图图像

C# 在WPF中创建复合位图图像,c#,wpf,bitmapimage,tiling,C#,Wpf,Bitmapimage,Tiling,我有三个位图图像,我想缝合在一起创建一个合成图像。要缝合在一起的三个图像按以下方式对齐: 图像的类型为System.Windows.Media.Imaging.BitmapImage。我已经看过以下内容,但它使用System.Drawing.Graphics来执行缝合。每次我想将位图图像粘贴在一起时,都无法将其转换为System.Drawing.Bitmap 是否有一种简单的方法将System.Windows.Media.Imaging.BitmaImage类型的三个图像缝合在一起?这取决于您

我有三个位图图像,我想缝合在一起创建一个合成图像。要缝合在一起的三个图像按以下方式对齐:

图像的类型为System.Windows.Media.Imaging.BitmapImage。我已经看过以下内容,但它使用System.Drawing.Graphics来执行缝合。每次我想将位图图像粘贴在一起时,都无法将其转换为System.Drawing.Bitmap


是否有一种简单的方法将System.Windows.Media.Imaging.BitmaImage类型的三个图像缝合在一起?

这取决于您想要的输出类型。这里有一些选择

绘图视觉

如果只需要将它们渲染为视觉效果,可以使用渲染器渲染这三个图像。然后,您可以根据您的用例以各种方式呈现视觉效果(例如,使用
VisualBrush

自定义元素

如果您需要一个可以直接放置在UI中的元素,可以创建一个扩展
FrameworkElement
的自定义元素

class CustomElement : FrameworkElement
{
    public ImageSource Image1
    {
        get { return (ImageSource)GetValue(Image1Property); }
        set { SetValue(Image1Property, value); }
    }
    public static readonly DependencyProperty Image1Property = DependencyProperty.Register("Image1", typeof(ImageSource), typeof(CustomElement),
        new FrameworkPropertyMetadata((ImageSource)null, FrameworkPropertyMetadataOptions.AffectsRender));

    public ImageSource Image2
    {
        get { return (ImageSource)GetValue(Image2Property); }
        set { SetValue(Image2Property, value); }
    }
    public static readonly DependencyProperty Image2Property = DependencyProperty.Register("Image2", typeof(ImageSource), typeof(CustomElement),
        new FrameworkPropertyMetadata((ImageSource)null, FrameworkPropertyMetadataOptions.AffectsRender));

    public ImageSource Image3
    {
        get { return (ImageSource)GetValue(Image3Property); }
        set { SetValue(Image3Property, value); }
    }
    public static readonly DependencyProperty Image3Property = DependencyProperty.Register("Image3", typeof(ImageSource), typeof(CustomElement),
        new FrameworkPropertyMetadata((ImageSource)null, FrameworkPropertyMetadataOptions.AffectsRender));

    protected override void OnRender(DrawingContext drawingContext)
    {
        drawingContext.DrawImage(Image1, new Rect(0.0, 0.0, ActualWidth, ActualHeight * 0.5));
        drawingContext.DrawImage(Image2, new Rect(0.0, ActualHeight * 0.5, ActualWidth * 0.75, ActualHeight * 0.5));
        drawingContext.DrawImage(Image3, new Rect(ActualWidth * 0.75, ActualHeight * 0.5, ActualWidth * 0.25, ActualHeight * 0.5));
    }
}
然后您可以这样使用它:

<local:CustomElement
    Image1="{Binding SomeImage}"
    Image2="{Binding SomeOtherImage}"
    Image3="http://stackoverflow.com/favicon.ico" />
创建图像源


如果出于某种原因需要将三个图像组合成一个
ImageSource
,则可以渲染成
DrawingVisual
(如上所述),然后将该视觉效果渲染成一个。这里有一些选择

绘图视觉

如果只需要将它们渲染为视觉效果,可以使用渲染器渲染这三个图像。然后,您可以根据您的用例以各种方式呈现视觉效果(例如,使用
VisualBrush

自定义元素

如果您需要一个可以直接放置在UI中的元素,可以创建一个扩展
FrameworkElement
的自定义元素

class CustomElement : FrameworkElement
{
    public ImageSource Image1
    {
        get { return (ImageSource)GetValue(Image1Property); }
        set { SetValue(Image1Property, value); }
    }
    public static readonly DependencyProperty Image1Property = DependencyProperty.Register("Image1", typeof(ImageSource), typeof(CustomElement),
        new FrameworkPropertyMetadata((ImageSource)null, FrameworkPropertyMetadataOptions.AffectsRender));

    public ImageSource Image2
    {
        get { return (ImageSource)GetValue(Image2Property); }
        set { SetValue(Image2Property, value); }
    }
    public static readonly DependencyProperty Image2Property = DependencyProperty.Register("Image2", typeof(ImageSource), typeof(CustomElement),
        new FrameworkPropertyMetadata((ImageSource)null, FrameworkPropertyMetadataOptions.AffectsRender));

    public ImageSource Image3
    {
        get { return (ImageSource)GetValue(Image3Property); }
        set { SetValue(Image3Property, value); }
    }
    public static readonly DependencyProperty Image3Property = DependencyProperty.Register("Image3", typeof(ImageSource), typeof(CustomElement),
        new FrameworkPropertyMetadata((ImageSource)null, FrameworkPropertyMetadataOptions.AffectsRender));

    protected override void OnRender(DrawingContext drawingContext)
    {
        drawingContext.DrawImage(Image1, new Rect(0.0, 0.0, ActualWidth, ActualHeight * 0.5));
        drawingContext.DrawImage(Image2, new Rect(0.0, ActualHeight * 0.5, ActualWidth * 0.75, ActualHeight * 0.5));
        drawingContext.DrawImage(Image3, new Rect(ActualWidth * 0.75, ActualHeight * 0.5, ActualWidth * 0.25, ActualHeight * 0.5));
    }
}
然后您可以这样使用它:

<local:CustomElement
    Image1="{Binding SomeImage}"
    Image2="{Binding SomeOtherImage}"
    Image3="http://stackoverflow.com/favicon.ico" />
创建图像源


如果出于某种原因需要将三个图像组合成一个
ImageSource
,除了在另一个答案中描述的选项外,还可以渲染成
DrawingVisual
(如上所述),然后将该视像渲染成,下面的代码将三个BitmapSource缝合到一个可写BitMap中:

public BitmapSource StitchBitmaps(BitmapSource b1, BitmapSource b2, BitmapSource b3)
{
    if (b1.Format != b2.Format || b1.Format != b3.Format)
    {
        throw new ArgumentException("All input bitmaps must have the same pixel format");
    }

    var width = Math.Max(b1.PixelWidth, b2.PixelWidth + b3.PixelWidth);
    var height = b1.PixelHeight + Math.Max(b2.PixelHeight, b3.PixelHeight);
    var wb = new WriteableBitmap(width, height, 96, 96, b1.Format, null);
    var stride1 = (b1.PixelWidth * b1.Format.BitsPerPixel + 7) / 8;
    var stride2 = (b2.PixelWidth * b2.Format.BitsPerPixel + 7) / 8;
    var stride3 = (b3.PixelWidth * b3.Format.BitsPerPixel + 7) / 8;
    var size = b1.PixelHeight * stride1;
    size = Math.Max(size, b2.PixelHeight * stride2);
    size = Math.Max(size, b3.PixelHeight * stride3);

    var buffer = new byte[size];
    b1.CopyPixels(buffer, stride1, 0);
    wb.WritePixels(
        new Int32Rect(0, 0, b1.PixelWidth, b1.PixelHeight),
        buffer, stride1, 0);

    b2.CopyPixels(buffer, stride2, 0);
    wb.WritePixels(
        new Int32Rect(0, b1.PixelHeight, b2.PixelWidth, b2.PixelHeight),
        buffer, stride2, 0);

    b3.CopyPixels(buffer, stride3, 0);
    wb.WritePixels(
        new Int32Rect(b2.PixelWidth, b1.PixelHeight, b3.PixelWidth, b3.PixelHeight),
        buffer, stride3, 0);

    return wb;
}

除了其他答案中描述的选项外,下面的代码将三个BitmapSource缝合到一个可写BitMap中:

public BitmapSource StitchBitmaps(BitmapSource b1, BitmapSource b2, BitmapSource b3)
{
    if (b1.Format != b2.Format || b1.Format != b3.Format)
    {
        throw new ArgumentException("All input bitmaps must have the same pixel format");
    }

    var width = Math.Max(b1.PixelWidth, b2.PixelWidth + b3.PixelWidth);
    var height = b1.PixelHeight + Math.Max(b2.PixelHeight, b3.PixelHeight);
    var wb = new WriteableBitmap(width, height, 96, 96, b1.Format, null);
    var stride1 = (b1.PixelWidth * b1.Format.BitsPerPixel + 7) / 8;
    var stride2 = (b2.PixelWidth * b2.Format.BitsPerPixel + 7) / 8;
    var stride3 = (b3.PixelWidth * b3.Format.BitsPerPixel + 7) / 8;
    var size = b1.PixelHeight * stride1;
    size = Math.Max(size, b2.PixelHeight * stride2);
    size = Math.Max(size, b3.PixelHeight * stride3);

    var buffer = new byte[size];
    b1.CopyPixels(buffer, stride1, 0);
    wb.WritePixels(
        new Int32Rect(0, 0, b1.PixelWidth, b1.PixelHeight),
        buffer, stride1, 0);

    b2.CopyPixels(buffer, stride2, 0);
    wb.WritePixels(
        new Int32Rect(0, b1.PixelHeight, b2.PixelWidth, b2.PixelHeight),
        buffer, stride2, 0);

    b3.CopyPixels(buffer, stride3, 0);
    wb.WritePixels(
        new Int32Rect(b2.PixelWidth, b1.PixelHeight, b3.PixelWidth, b3.PixelHeight),
        buffer, stride3, 0);

    return wb;
}

用于获取源图像的像素缓冲区,并将其放入可写的位图中。@Clemens是的,我也是这么想的。我想知道是否有更简单的方法来做这样一个基本的操作。我会这样做,并将代码粘贴到这里。用于获取源图像的像素缓冲区,并将它们放入可写位图中。@Clemens是的,这也是我的想法。我想知道是否有更简单的方法来做这样一个基本的操作。我会把代码贴在这里,谢谢!这很有魅力。但是我有一个问题,当你计算步幅时,魔法数字7做了什么?当每像素位数不是8的整数倍时,有必要计算每个扫描线(即步幅)的正确字节数。常见的位图格式是每像素24位或32位,因此您也可以将步幅计算为宽度*bpp/8。谢谢!这很有魅力。但是我有一个问题,当你计算步幅时,魔法数字7做了什么?当每像素位数不是8的整数倍时,有必要计算每个扫描线(即步幅)的正确字节数。常见的位图格式是每像素24位或32位,因此您也可以将步幅计算为宽度*bpp/8。