Windows 商店应用程序 - RenderTargetBitmap 创建大小不正确的位图
Windows Store App - RenderTargetBitmap creating a bitmap of incorrect size
我正在尝试在我的应用拍摄的照片上添加水印。我能想到的最简单的方法是使用 FrameworkElement
构建图层,然后使用 RenderTargetBitmap
创建水印图像。
这是我的 XAML.
的示例
<ScrollViewer x:Name="Zoom" Grid.Column="1" HorizontalScrollMode="Enabled" VerticalScrollMode="Enabled" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" ZoomMode="Enabled">
<Border x:Name="BgBorder">
<Grid x:Name="ImageGird" SizeChanged="ImageGird_SizeChanged">
<Grid x:Name="CaptureGird">
<Image x:Name="CapturedImage" Stretch="None" Source="ms-appx:///Assets/Photo.jpg" />
<StackPanel x:Name="Watermark" VerticalAlignment="Top" HorizontalAlignment="Left" Background="#6FFFFFFF" Margin="10">
<TextBlock Text="Name" Foreground="Black" Margin="10,2.5,10,2.5" />
<TextBlock Text="12345" Foreground="Black" Margin="10,2.5,10,2.5"/>
<TextBlock Text="54321" Foreground="Black" Margin="10,2.5,10,2.5" />
</StackPanel>
</Grid>
</Grid>
</Border>
</ScrollViewer>
由于图像的分辨率,他们需要它被包裹在 ScrollViewer
中,因此它可以缩小,但是当我尝试使用下面的代码创建此图像的位图时,渲染的位图是小于 FrameworkElement
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
var displayI = DisplayInformation.GetForCurrentView();
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(ImageGird, (int)ImageGird.ActualWidth, (int)ImageGird.ActualHeight);
IBuffer pixels = await renderTargetBitmap.GetPixelsAsync();
CapturedImage2.Source = renderTargetBitmap;
Debug.WriteLine("Button_Click: ImageGrid: " + ImageGird.ActualWidth + "x" + ImageGird.ActualHeight + " RenderTargetBitmap: " + renderTargetBitmap.PixelWidth + "x" + renderTargetBitmap.PixelHeight);
}
catch (Exception )
{
}
}
调试输出为
Button_Click: ImageGrid: 5344x3008 RenderTargetBitmap: 4096x2306
谁能告诉我为什么渲染的位图比我创建它的实际元素小得多?
还有没有更好的方法给图片加水印?
ActualWidth 和 ActualHeight 是计算值。因此,特别是对于网格,这些值可能会随着时间的推移而改变是有道理的。我的猜测是,当您设置图像源时,实际宽度和高度正在重新计算。
我认为您应该能够使堆栈面板成为图像的子图像并将其相对于父图像定位,这将使它适用于可变图像尺寸。您还可以尝试在网格上设置一些约束并使用这些约束来确定位图大小,而不是使用实际的 width/height.
看看 Win2D 并使用离屏绘图。
感谢@A.J.Bauer 将我指向 Win2D,我已经设法相当优雅地解决了这个问题。
/// <summary>
/// Create a watermarked image from an image stream
/// </summary>
/// <param name="sender">Jpeg image stream.</param>
private async Task<ImageSource> CreateWaterMarkedImage(IRandomAccessStream stream)
{
// Ensure our stream is at the beginning.
stream.Seek(0);
// Create our Win2D in memory renderer.
CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget offscreen = new CanvasRenderTarget(device, (float)ImageGird.ActualWidth, (float)ImageGird.ActualHeight, 96);
// Create our Win2D bitmap
CanvasBitmap bmp = await CanvasBitmap.LoadAsync(offscreen, stream, 96);
// Create a text formatter for our watermark
var format = new CanvasTextFormat()
{
FontSize = 40,
HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top,
WordWrapping = CanvasWordWrapping.Wrap,
FontFamily = "Arial",
FontWeight = FontWeights.SemiBold
};
// Get a Win2D drawing session instance
using (CanvasDrawingSession ds = offscreen.CreateDrawingSession())
{
// Layer our resulting Watermarked image.
ds.DrawImage(bmp);
// Create the Win2D text layout so we can get the bounds.
var tl = new CanvasTextLayout(ds, "Name\r\n12345\r\n54321", format, 1000, 1000);
// Create a background for the text
ds.FillRectangle(10, 10, (float)tl.DrawBounds.Width + 20f, (float)tl.DrawBounds.Height + 20f, new Color() { A = 0x6F, R = 0xFF, G = 0xFF, B = 0xFF });
// Add the text layout.
ds.DrawTextLayout(tl, 10, 10, Colors.Black);
// Clear up the memory.
tl.Dispose();
}
// Create our bitmap so we can return an ImageSource
BitmapImage im = new BitmapImage();
using (InMemoryRandomAccessStream oStream = new InMemoryRandomAccessStream())
{
// Save the Win2D canvas renderer a stream.
await offscreen.SaveAsync(oStream, CanvasBitmapFileFormat.Jpeg, 1.0f);
stream.Seek(0);
// Stream our Win2D pixels into the Bitmap
await im.SetSourceAsync(oStream);
Debug.WriteLine("CreateWaterMarkedImage: ImageGrid: " + ImageGird.ActualWidth + "x" + ImageGird.ActualHeight + " Bitmap: " + im.PixelWidth + "x" + im.PixelHeight);
}
// Tidy Up.
format.Dispose();
bmp.Dispose();
offscreen.Dispose();
device.Dispose();
return im;
}
调试结果给了我。
CreateWaterMarkedImage: ImageGrid: 5344x3008 Bitmap: 5344x3008
如果设置 Image
UIElement
的来源,则会显示带水印的图像。
CapturedImage2.Source = await CreateWaterMarkedImage(photoStream);
这个示例非常适合我的需要,但是如果您使用 Win2D XAML 控件,任何人都应该能够轻松地适应它。
我正在尝试在我的应用拍摄的照片上添加水印。我能想到的最简单的方法是使用 FrameworkElement
构建图层,然后使用 RenderTargetBitmap
创建水印图像。
这是我的 XAML.
的示例 <ScrollViewer x:Name="Zoom" Grid.Column="1" HorizontalScrollMode="Enabled" VerticalScrollMode="Enabled" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" ZoomMode="Enabled">
<Border x:Name="BgBorder">
<Grid x:Name="ImageGird" SizeChanged="ImageGird_SizeChanged">
<Grid x:Name="CaptureGird">
<Image x:Name="CapturedImage" Stretch="None" Source="ms-appx:///Assets/Photo.jpg" />
<StackPanel x:Name="Watermark" VerticalAlignment="Top" HorizontalAlignment="Left" Background="#6FFFFFFF" Margin="10">
<TextBlock Text="Name" Foreground="Black" Margin="10,2.5,10,2.5" />
<TextBlock Text="12345" Foreground="Black" Margin="10,2.5,10,2.5"/>
<TextBlock Text="54321" Foreground="Black" Margin="10,2.5,10,2.5" />
</StackPanel>
</Grid>
</Grid>
</Border>
</ScrollViewer>
由于图像的分辨率,他们需要它被包裹在 ScrollViewer
中,因此它可以缩小,但是当我尝试使用下面的代码创建此图像的位图时,渲染的位图是小于 FrameworkElement
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
var displayI = DisplayInformation.GetForCurrentView();
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(ImageGird, (int)ImageGird.ActualWidth, (int)ImageGird.ActualHeight);
IBuffer pixels = await renderTargetBitmap.GetPixelsAsync();
CapturedImage2.Source = renderTargetBitmap;
Debug.WriteLine("Button_Click: ImageGrid: " + ImageGird.ActualWidth + "x" + ImageGird.ActualHeight + " RenderTargetBitmap: " + renderTargetBitmap.PixelWidth + "x" + renderTargetBitmap.PixelHeight);
}
catch (Exception )
{
}
}
调试输出为
Button_Click: ImageGrid: 5344x3008 RenderTargetBitmap: 4096x2306
谁能告诉我为什么渲染的位图比我创建它的实际元素小得多?
还有没有更好的方法给图片加水印?
ActualWidth 和 ActualHeight 是计算值。因此,特别是对于网格,这些值可能会随着时间的推移而改变是有道理的。我的猜测是,当您设置图像源时,实际宽度和高度正在重新计算。
我认为您应该能够使堆栈面板成为图像的子图像并将其相对于父图像定位,这将使它适用于可变图像尺寸。您还可以尝试在网格上设置一些约束并使用这些约束来确定位图大小,而不是使用实际的 width/height.
看看 Win2D 并使用离屏绘图。
感谢@A.J.Bauer 将我指向 Win2D,我已经设法相当优雅地解决了这个问题。
/// <summary>
/// Create a watermarked image from an image stream
/// </summary>
/// <param name="sender">Jpeg image stream.</param>
private async Task<ImageSource> CreateWaterMarkedImage(IRandomAccessStream stream)
{
// Ensure our stream is at the beginning.
stream.Seek(0);
// Create our Win2D in memory renderer.
CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget offscreen = new CanvasRenderTarget(device, (float)ImageGird.ActualWidth, (float)ImageGird.ActualHeight, 96);
// Create our Win2D bitmap
CanvasBitmap bmp = await CanvasBitmap.LoadAsync(offscreen, stream, 96);
// Create a text formatter for our watermark
var format = new CanvasTextFormat()
{
FontSize = 40,
HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top,
WordWrapping = CanvasWordWrapping.Wrap,
FontFamily = "Arial",
FontWeight = FontWeights.SemiBold
};
// Get a Win2D drawing session instance
using (CanvasDrawingSession ds = offscreen.CreateDrawingSession())
{
// Layer our resulting Watermarked image.
ds.DrawImage(bmp);
// Create the Win2D text layout so we can get the bounds.
var tl = new CanvasTextLayout(ds, "Name\r\n12345\r\n54321", format, 1000, 1000);
// Create a background for the text
ds.FillRectangle(10, 10, (float)tl.DrawBounds.Width + 20f, (float)tl.DrawBounds.Height + 20f, new Color() { A = 0x6F, R = 0xFF, G = 0xFF, B = 0xFF });
// Add the text layout.
ds.DrawTextLayout(tl, 10, 10, Colors.Black);
// Clear up the memory.
tl.Dispose();
}
// Create our bitmap so we can return an ImageSource
BitmapImage im = new BitmapImage();
using (InMemoryRandomAccessStream oStream = new InMemoryRandomAccessStream())
{
// Save the Win2D canvas renderer a stream.
await offscreen.SaveAsync(oStream, CanvasBitmapFileFormat.Jpeg, 1.0f);
stream.Seek(0);
// Stream our Win2D pixels into the Bitmap
await im.SetSourceAsync(oStream);
Debug.WriteLine("CreateWaterMarkedImage: ImageGrid: " + ImageGird.ActualWidth + "x" + ImageGird.ActualHeight + " Bitmap: " + im.PixelWidth + "x" + im.PixelHeight);
}
// Tidy Up.
format.Dispose();
bmp.Dispose();
offscreen.Dispose();
device.Dispose();
return im;
}
调试结果给了我。
CreateWaterMarkedImage: ImageGrid: 5344x3008 Bitmap: 5344x3008
如果设置 Image
UIElement
的来源,则会显示带水印的图像。
CapturedImage2.Source = await CreateWaterMarkedImage(photoStream);
这个示例非常适合我的需要,但是如果您使用 Win2D XAML 控件,任何人都应该能够轻松地适应它。