在 WPF 中创建复合位图图像
Create a Composite BitmapImage in WPF
我有三个 BitmapImage,我想将它们拼接在一起以创建合成图像。待拼接的三张图片按如下方式对齐:
图片的类型是System.Windows.Media.Imaging.BitmapImage。我看过下面的solution,但它使用System.Drawing.Graphics进行拼接。每次我想将它们拼接在一起时,我发现将我的 BitmapImage 转换为 System.Drawing.Bitmap 是不直观的。
有没有一种简单的方法可以将三张 System.Windows.Media.Imaging.BitmapImage 类型的图像拼接在一起?
这实际上取决于您想要的输出类型。这里有一些选项。
视觉绘图
如果您只需要将它们渲染成视觉效果,您可以使用 DrawingVisual
并渲染这三个图像。然后,您可以根据您的用例以各种方式呈现视觉效果(例如,使用 VisualBrush
)。
Rect bounds = new Rect(0.0, 0.0, 400.0, 300.0);
DrawingVisual visual = new DrawingVisual();
DrawingContext dc = visual.RenderOpen();
dc.DrawImage(mImage, new Rect(bounds.X, bounds.Y, bounds.Width, bounds.Height * 0.5));
dc.DrawImage(mImage, new Rect(bounds.X, bounds.Y + bounds.Height * 0.5, bounds.Width * 0.75, bounds.Height * 0.5));
dc.DrawImage(mImage, new Rect(bounds.X + bounds.Width * 0.75, bounds.Y + bounds.Height * 0.5, bounds.Width * 0.25, bounds.Height * 0.5));
dc.Close();
自定义元素
如果您需要一个可以直接放在 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://whosebug.com/favicon.ico" />
网格中的图像
您始终可以选择将三个 Image
控件放在 Grid
中。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
Grid.ColumnSpan="2"
Source="{Binding SomeImage}" />
<Image
Grid.Row="1"
Source="{Binding SomeOtherImage}" />
<Image
Grid.Row="1"
Grid.Column="1"
Source="http://stackexchange.com/favicon.ico" />
</Grid>
创建图像源
如果您出于某种原因需要将三个图像组合成一个 ImageSource
,您可以渲染成 DrawingVisual
(如上所述),然后将该视觉效果渲染成 RenderTargetBitmap
.
除了其他答案中描述的选项之外,下面的代码将三个 BitmapSource 拼接成一个 WriteableBitmap:
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;
}
我有三个 BitmapImage,我想将它们拼接在一起以创建合成图像。待拼接的三张图片按如下方式对齐:
图片的类型是System.Windows.Media.Imaging.BitmapImage。我看过下面的solution,但它使用System.Drawing.Graphics进行拼接。每次我想将它们拼接在一起时,我发现将我的 BitmapImage 转换为 System.Drawing.Bitmap 是不直观的。
有没有一种简单的方法可以将三张 System.Windows.Media.Imaging.BitmapImage 类型的图像拼接在一起?
这实际上取决于您想要的输出类型。这里有一些选项。
视觉绘图
如果您只需要将它们渲染成视觉效果,您可以使用 DrawingVisual
并渲染这三个图像。然后,您可以根据您的用例以各种方式呈现视觉效果(例如,使用 VisualBrush
)。
Rect bounds = new Rect(0.0, 0.0, 400.0, 300.0);
DrawingVisual visual = new DrawingVisual();
DrawingContext dc = visual.RenderOpen();
dc.DrawImage(mImage, new Rect(bounds.X, bounds.Y, bounds.Width, bounds.Height * 0.5));
dc.DrawImage(mImage, new Rect(bounds.X, bounds.Y + bounds.Height * 0.5, bounds.Width * 0.75, bounds.Height * 0.5));
dc.DrawImage(mImage, new Rect(bounds.X + bounds.Width * 0.75, bounds.Y + bounds.Height * 0.5, bounds.Width * 0.25, bounds.Height * 0.5));
dc.Close();
自定义元素
如果您需要一个可以直接放在 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://whosebug.com/favicon.ico" />
网格中的图像
您始终可以选择将三个 Image
控件放在 Grid
中。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
Grid.ColumnSpan="2"
Source="{Binding SomeImage}" />
<Image
Grid.Row="1"
Source="{Binding SomeOtherImage}" />
<Image
Grid.Row="1"
Grid.Column="1"
Source="http://stackexchange.com/favicon.ico" />
</Grid>
创建图像源
如果您出于某种原因需要将三个图像组合成一个 ImageSource
,您可以渲染成 DrawingVisual
(如上所述),然后将该视觉效果渲染成 RenderTargetBitmap
.
除了其他答案中描述的选项之外,下面的代码将三个 BitmapSource 拼接成一个 WriteableBitmap:
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;
}