RenderTargetBitmap Image Sliding - c#

I'm having a problem with RenderTargetBitmap whenever I render canvas and clear its children and set the rendered bitmap as background of canvas it slide toward bottom right.
can't insert images until 10 reputation :(.
WPF:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="700"
KeyDown="Window_KeyDown">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="Black" BorderThickness="1" Grid.Row="1" Grid.Column="1">
<Canvas x:Name="Pad">
<Rectangle Height="100" Width="100" Fill="Red" Canvas.Left="10" Canvas.Top="10"></Rectangle>
</Canvas>
</Border>
</Grid>
</Window>
c# code:
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
RenderTargetBitmap rendrer = new RenderTargetBitmap(Convert.ToInt32(Pad.ActualWidth), Convert.ToInt32(Pad.ActualHeight), 96, 96, PixelFormats.Pbgra32);
rendrer.Render(Pad);
Pad.Background = new ImageBrush(rendrer);
Pad.Children.Clear();
}
}
}

To avoid any offset problems with drawing a Visual into a RenderTargetBitmap, you may use an intermediate DrawingVisual:
var rect = new Rect(Pad.RenderSize);
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
dc.DrawRectangle(new VisualBrush(Pad), null, rect);
}
var bitmap = new RenderTargetBitmap(
(int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Default);
bitmap.Render(visual);
Pad.Background = new ImageBrush(bitmap);
Pad.Children.Clear();
Note that without setting any further properties of the ImageBrush (like e.g. its Viewport), it will fill the entire area of the Rectangle. For details, see TileBrush Overview.

Your primary problem stems from the fact that, due to the 1-pixel border around the Canvas, its VisualOffset vector is (1,1). Thus, any visual effect, like the background brush, will be applied at that offset. When you render the visual into a bitmap, it captures the present appearance, and then when you set the bitmap as the brush, it gets shifted.
Ironically, one of the easiest ways to fix this is to insert another <Border/> element into your XAML:
<Border BorderBrush="Black" BorderThickness="1" Grid.Row="1" Grid.Column="1">
<Border>
<Canvas x:Name="Pad">
<Rectangle Height="100" Width="100" Fill="Red" Canvas.Left="10" Canvas.Top="10"/>
</Canvas>
</Border>
</Border>
Then the offset caused by the outer <Border/> element is handled by the new <Border/> element's transform, rather than being applied to the <Canvas/> element.
That change alone will almost fix your code completely. However, there's one other little artifact that you may still notice: every time you render the visual, it gets just a teensy bit blurrier. This is because the default value for the Brush object's Stretch property is Stretch.Fill, and because your <Canvas/> element is not precisely an integral width or height, the bitmap (which necessarily does have integral width and height) gets stretched just a teensy bit when rendered. With each iteration, this becomes more and more apparent.
You can fix that by setting the Stretch property to Stretch.None. At the same time, you'll also want to set the brush's alignment to Left and Top:
private void Window_KeyDown(object sender, KeyEventArgs e)
{
RenderTargetBitmap renderer = new RenderTargetBitmap(
Convert.ToInt32(Pad.ActualWidth), Convert.ToInt32(Pad.ActualHeight), 96, 96, PixelFormats.Pbgra32);
renderer.Render(Pad);
ImageBrush brush = new ImageBrush(renderer);
brush.AlignmentX = AlignmentX.Left;
brush.AlignmentY = AlignmentY.Top;
brush.Stretch = Stretch.None;
Pad.Background = brush;
Pad.Children.Clear();
}
The defaults are Center, which again incurs the rounding error and will cause both movement and blurring of the image after repeated iterations of the process.
With the above changes, I found a perfectly stable image, regardless of the number of iterations.
The "wrap in a border" idea came from here: https://blogs.msdn.microsoft.com/jaimer/2009/07/03/rendertargetbitmap-tips/
On that page you'll find a more general-purpose solution which does not require modification of the actual XAML. In your example above, the "wrap in a border" approach seems like a reasonable work-around, but it is admittedly not as clean as forcing an unadorned context into which you can render the visual, as shown on that blog page.

Related

C# WPF Images not displaying

In my code, I have 3 images that are empty and then I set the source in the code behind. The relative path I'm giving them is the right one but the images are not displaying anyway. I got no clue how to fix this.
Xaml
<Border BorderThickness="1" Margin="44,135,433,248" BorderBrush="#FF000000">
<Image x:Name="imageHelmet" HorizontalAlignment="Left" Height="116" Margin="-1" VerticalAlignment="Top" Width="127" MouseEnter="helmet_MouseEnter"/>
</Border>
Behind
string source = #"..\..\..\Images\" + piece.Link;
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri(source, UriKind.Relative);
bmp.EndInit();
imageChestplate.Source = bmp;
This is just one of them, but the rest are the same.
The image is likely hidden due to the margin of the border, at least that's what I saw when I attempted to recreate your problem.
Here, the Margin Property of the Border is set to 44,135,433,248. This means that there is a margin of 433 on the right side, and 248 on the bottom.
<Border BorderThickness="1" Margin="44,135,433,248" BorderBrush="#FF000000">
The edge of the window counts as something getting within the margin, so if the window is too small, it will not leave enough space for the margin, and as such the margin will be pushed over the image, hiding it.
To remedy this situation, change the right and bottom values in the margin to 0, and set the Border's HorizontalAlignment to Left, and VerticalAlignment to Top.
<Border BorderThickness="1" Margin="44,135,0,0" BorderBrush="#FF000000" HorizontalAlignment="Left" VerticalAlignment="Top">
<Image x:Name="imageHelmet" HorizontalAlignment="Left" Height="116" Margin="-1" VerticalAlignment="Top" Width="127" MouseEnter="helmet_MouseEnter"/>
</Border>
This way, there's no margin on the right and bottom sides to get pushed over the image, and the alignment makes it so the left and top alignments are used correctly.

Irregular behavior - XAML / UWP Community Toolkit Scale animation

Problem : I am using UWP Community Toolkit Scale animation and it works as expected for most of the images in the GridView, but for some the image goes out of bounds . (Please see the image below)
I have detected that the issue happens when the image width is more than 2x (2 times) the height of the image. That is when the image is very wide.
Code
I am using a user control as data template
Xaml :
<!-- Grid View -->
<GridView x:Name="gridView" SelectionChanged="gridView_SelectionChanged">
<GridView.ItemTemplate>
<DataTemplate>
<local:GridViewMenu/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<!-- GridViewMenu User Control markup -->
<Grid>
<StackPanel>
<Image Source="{Binding webformatURL}" Stretch="UniformToFill" PointerEntered="image_PointerEntered" PointerExited="image_PointerExited"/>
</StackPanel>
</Grid>
C# Code :
private void image_PointerEntered(object sender, PointerRoutedEventArgs e)
{
Image img = sender as Image;
img.Scale(centerX: (float)(grid.ActualWidth / 2),
centerY: 100,
scaleX: 1.2f,
scaleY: 1.2f,
duration: 500, delay: 0).StartAsync();
}
private void image_PointerExited(object sender, PointerRoutedEventArgs e)
{
Image img = sender as Image;
img.Scale(centerX: (float)(grid.ActualWidth / 2),
centerY: 100,
scaleX: 1f,
scaleY: 1f,
duration: 500, delay: 0).StartAsync();
}
Result (Top left image is not scaling as expected, that is, it is going out of bounds)
How can I solve this issue ?
The scale animation of UWP Community Toolkit package actually use the CompositeTransform class for scaling. According to the description of Transforms and layout section:
Because layout comes first, you'll sometimes get unexpected results if you transform elements that are in a Grid cell or similar layout container that allocates space during layout. The transformed element may appear truncated or obscured because it's trying to draw into an area that didn't calculate the post-transform dimensions when dividing space within its parent container.
So that the parts overflow the bound that being truncated are unexpected. In another words, the image goes out is the transform expected. The current way you are using to meet your requirements is not reliable. If you change width-height ratio of GridViewMenu to 1.0 , you may find more images that width-height ratio larger than 1.0 will go out.
For a solution inside GridView, you could consider to use the ScrollViewer to zoom in the image instead, which can ensure the image is limited in a fixed area. For example:
<Grid x:Name="grid">
<ScrollViewer
x:Name="currentscroll"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden">
<Image
x:Name="myImage"
Width="300"
Height="180"
PointerEntered="image_PointerEntered"
PointerExited="image_PointerExited"
Source="{Binding webformatURL}"
Stretch="UniformToFill">
</Image>
</ScrollViewer>
</Grid>
Code behind:
private void image_PointerEntered(object sender, PointerRoutedEventArgs e)
{
currentscroll.ChangeView(0, 0, 1.2f );
}
private void image_PointerExited(object sender, PointerRoutedEventArgs e)
{
currentscroll.ChangeView(0, 0, 1.0f);
}
You can try to use clipping:
<Grid>
<StackPanel>
<Image Source="{Binding webformatURL}" Stretch="UniformToFill"
PointerEntered="image_PointerEntered"
PointerExited="image_PointerExited">
<Image.Clip>
<RectangleGeometry Rect="0,0,300,150" />
</Image.Clip>
</Image>
</StackPanel>
</Grid>
300 and 150 would be the width and height of the grid item.
Otherwise it looks like a bug in the UWP Community Toolkit, it would be best to report it as an issue on GitHub.

Horizontal image scaling with ScaleTransform in WPF

I have an image in a WPF window with the default Stretch setting, Uniform, and am making an attempt to make it fill the screen horizontally. I do not wish to use a different Stretch setting as this is supposed to a learning experience. The image dimensions being loaded are 420x800. This is the XAML for the window..
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Red" Height="1200" Width="840">
<Image Name="Image" Source="{Binding SourceUri}">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="Scale" />
<TranslateTransform x:Name="Translate" />
</TransformGroup>
</Image.RenderTransform>
</Image>
</Window>
On the code-behind, I am attempting to calculate the scaling to zoom the image to fill the horizontal screen and I am using translate transform the move it to the center of the screen. The following bit of code is obviously wrong...
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfApplication1 {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
DataContext = this;
double ImageWidth = 420;
Scale.ScaleX = Width / ImageWidth;
Translate.X = -(ImageWidth / 2);
}
public string SourceUri {
get {
return #"C:\Users\Roel\Desktop\test.png";
}
}
}
}
I am attempting to understand how stretching and transformations are working together but I am having difficulty with this. I would appreciate all insights, even references to detailed explanations, as I have trouble finding any informational source explaining clearly and concisely how the transformations are applied.
You would usually just do this:
<Image Name="Image" Source="{Binding SourceUri}" Stretch="Fill"/>
In case you really need to calculate the stretch transformation manually, you would only need a ScaleTransform, no TranslateTransform, and you would put that into the LayoutTransform of the Image control. Moreover, the Image control would have to be placed into a Grid, which provides the size of the Windows's "client area". You can't calculate anything based on the Window's Width (or ActualWidth) as that includes the width of the Window's borders.
<Grid SizeChanged="Grid_SizeChanged">
<Image Name="image" Source="{Binding SourceUri}">
<Image.LayoutTransform>
<ScaleTransform x:Name="scale"/>
</Image.LayoutTransform>
</Image>
</Grid>
In the Grid's SizeChanged handler you would calculate the scaling as shown below.
private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
scale.ScaleX = e.NewSize.Width / image.Source.Width;
scale.ScaleY = e.NewSize.Height / image.Source.Height;
}

WPF databinding problem

I have a grid inside a canvas on a tab.
The grid contains a large bitmap image,
I have(tried to) bound the size of the grid to the size of the tab and also have a five pixel margin around the grid.
imageTab.cs
public ImageTab(SendInfo sendInfo, int numImge, int numAccs)
{
imageDisplay = new ImageDisplay(sendInfo, numImge, numAccs);
imageDisplay.ClipToBounds = true;
CreateCanvas();
}
private void CreateCanvas()
{
Canvas canvas = new Canvas();
canvas.Children.Add(imageDisplay);
this.AddChild(canvas);
}
ImageDisplay.xaml
<UserControl x:Class="MyProj.ImageDisplay">
<Grid Margin="5,5,5,5" Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=TabControl, AncestorLevel=1}, Path=ActualHeight}">
<Image/>
</Grid>
</UserControl>
The grid comes off the bottom of the tab area slightly causing the bottom of the image to be cut off.
Is there a problem with my databinding, do I need to apply some sort of offset to it? (size of tab - 10pixels for the margin?)
You don't need to set the Height property at all (also realize that it is incorrect to do so as you have it when you consider the 5 pixel margin, i.e., it would be off by 10 pixels).
Just leave VerticalAlignment and HorizontalAlignment at their default values (which is Stretch) to get the effect you are after here.
Try this on a new Window to see what I mean:
<Window x:Class="WpfApplication9.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="438" Width="587" Background="Pink">
<Grid Background="Black" Margin="5">
</Grid>
</Window>
The grid here will be black and will always stretch to the size of the window, using a 5 pixel margin which you will see because the Window's back color is pink.

RenderTargetBitmap and Viewport3D - Quality Issues

I'm wanting to export a 3D scene from a Viewport3D to a bitmap.
The obvious way to do this would be to use RenderTargetBitmap -- however when I this the quality of the exported bitmap is significantly lower than the on-screen image. Looking around on the internet, it seems that RenderTargetBitmap doesn't take advantage of hardware rendering. Which means that the rendering is done at Tier 0. Which means no mip-mapping etc, hence the reduced quality of the exported image.
Does anyone know how to export a bitmap of a Viewport3D at on-screen quality?
Clarification
Though the example given below doesn't show this, I need to eventually export the bitmap of the Viewport3D to a file. As I understand the only way to do this is to get the image into something that derives from BitmapSource. Cplotts below shows that increasing the quality of the export using RenderTargetBitmap improves the image, but as the rendering is still done in software, it is prohibitively slow.
Is there a way to export a rendered 3D scene to a file, using hardware rendering? Surely that should be possible?
You can see the problem with this xaml:
<Window x:Class="RenderTargetBitmapProblem.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="400" Width="500">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Viewport3D Name="viewport3D">
<Viewport3D.Camera>
<PerspectiveCamera Position="0,0,3"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="White"/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="-1,-10,0 1,-10,0 -1,20,0 1,20,0"
TextureCoordinates="0,1 0,0 1,1 1,0"
TriangleIndices="0,1,2 1,3,2"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush ImageSource="http://www.wyrmcorp.com/galleries/illusions/Hermann%20Grid.png"
TileMode="Tile" Viewport="0,0,0.25,0.25"/>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
<ModelVisual3D.Transform>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis="1,0,0" Angle="-82"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
</ModelVisual3D.Transform>
</ModelVisual3D>
</Viewport3D>
<Image Name="rtbImage" Visibility="Collapsed"/>
<Button Grid.Row="1" Click="Button_Click">RenderTargetBitmap!</Button>
</Grid>
</Window>
And this code:
private void Button_Click(object sender, RoutedEventArgs e)
{
RenderTargetBitmap bmp = new RenderTargetBitmap((int)viewport3D.ActualWidth,
(int)viewport3D.ActualHeight, 96, 96, PixelFormats.Default);
bmp.Render(viewport3D);
rtbImage.Source = bmp;
viewport3D.Visibility = Visibility.Collapsed;
rtbImage.Visibility = Visibility.Visible;
}
There is no setting on RenderTargetBitmap to tell it to render using hardware, so you will have to fall back to using Win32 or DirectX. I would recommend using the DirectX technique given in this article. The following code from the article and shows how it can be done (this is C++ code):
extern IDirect3DDevice9* g_pd3dDevice;
Void CaptureScreen()
{
IDirect3DSurface9* pSurface;
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, pSurface);
D3DXSaveSurfaceToFile("Desktop.bmp",D3DXIFF_BMP,pSurface,NULL,NULL);
pSurface->Release();
}
You can create the Direct3D device corresponding to the place where the WPF content is being rendered as follows:
Calling Visual.PointToScreen on a point within your onscreen image
Calling MonitorFromPoint in User32.dll to get the hMonitor
Calling Direct3DCreate9 in d3d9.dll to get a pD3D
Calling pD3D->GetAdapterCount() to count adapters
Iterating from 0 to count-1 and calling pD3D->GetAdapterMonitor() and comparing with the previously retrieved hMonitor to determine the adapter index
Calling pD3D->CreateDevice() to create the device itself
I would probably do most of this in a separate library coded in C++/CLR because that approach is familiar to me, but you may find it easy to translate it to pure C# and managed code using using SlimDX. I haven't tried that yet.
I don't know what mip-mapping is (or whether the software renderer does that and/or multi-level anti-aliasing), but I do recall a post by Charles Petzold a while ago that was all about printing hi-res WPF 3D visuals.
I tried it out with your sample code and it appears to work great. So, I assume you just needed to scale things up a bit.
You need to set Stretch to None on the rtbImage and modify the Click event handler as follows:
private void Button_Click(object sender, RoutedEventArgs e)
{
// Scale dimensions from 96 dpi to 600 dpi.
double scale = 600 / 96;
RenderTargetBitmap bmp =
new RenderTargetBitmap
(
(int)(scale * (viewport3D.ActualWidth + 1)),
(int)(scale * (viewport3D.ActualHeight + 1)),
scale * 96,
scale * 96,
PixelFormats.Default
);
bmp.Render(viewport3D);
rtbImage.Source = bmp;
viewport3D.Visibility = Visibility.Collapsed;
rtbImage.Visibility = Visibility.Visible;
}
Hope that solves your problem!
I think you were getting a blank screen for a couple reasons. First, the VisualBrush needed to be pointing to a visible Visual. Second, maybe you forgot that the RectangleGeometry needed to have dimensions (I know I did at first).
I did see some odd things that I don't quite understand. That is, I do not understand why I had to set AlignmentY to Bottom on the VisualBrush.
Other than that, I think it works ... and I think you should easily be able to modify the code for your real situation.
Here is the button click event handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
GeometryDrawing geometryDrawing = new GeometryDrawing();
geometryDrawing.Geometry =
new RectangleGeometry
(
new Rect(0, 0, viewport3D.ActualWidth, viewport3D.ActualHeight)
);
geometryDrawing.Brush =
new VisualBrush(viewport3D)
{
Stretch = Stretch.None,
AlignmentY = AlignmentY.Bottom
};
DrawingImage drawingImage = new DrawingImage(geometryDrawing);
image.Source = drawingImage;
}
Here is Window1.xaml:
<Window
x:Class="RenderTargetBitmapProblem.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="400"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400"/>
</Grid.ColumnDefinitions>
<Viewport3D
x:Name="viewport3D"
Grid.Row="0"
Grid.Column="0"
>
<Viewport3D.Camera>
<PerspectiveCamera Position="0,0,3"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="White"/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D
Positions="-1,-10,0 1,-10,0 -1,20,0 1,20,0"
TextureCoordinates="0,1 0,0 1,1 1,0"
TriangleIndices="0,1,2 1,3,2"
/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource="http://www.wyrmcorp.com/galleries/illusions/Hermann%20Grid.png"
TileMode="Tile"
Viewport="0,0,0.25,0.25"
/>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
<ModelVisual3D.Transform>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis="1,0,0" Angle="-82"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
</ModelVisual3D.Transform>
</ModelVisual3D>
</Viewport3D>
<Image
x:Name="image"
Grid.Row="0"
Grid.Column="0"
/>
<Button Grid.Row="1" Click="Button_Click">Render!</Button>
</Grid>
</Window>
I think the issue here indeed is that the software renderer for WPF does not perform mip-mapping and multi-level anti aliasing. Rather than using RanderTargetBitmap, you may be able to create an DrawingImage whose ImageSource is the 3D scene you want to render. In theory, the hardware render should produce the scene image, which you can then programmatically extract from the DrawingImage.
Using SlimDX, try accessing the DirectX surface that the ViewPort3D renders to,
then performing a read-pixel to read the buffer from the graphics card's pixel buffer into regular memory.
Once you have the (unmanaged) buffer, copy it into an existing writable bitmap using unsafe code or marshalling.
I've also had a few useful answers to this question over at the Windows Presentation Foundation forums at http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/50989d46-7240-4af5-a8b5-d034cb2a498b/.
In particular, I'm going to try these two answers, both from Marco Zhou:
Or you could try rendering the
Viewport3D into a off-screen
HwndSource, and then grab its HWND,
and feed it into Bitblt function . The
Bitblt will copy what's already
rendered by the hardware rasterizer
back to your own in memory buffer. I
am not trying this method myself, but
it's worth trying, and theoretically
speaking, it should work.
and
I think one easy way without pinvoking
into the WIN32 API is to place the
Viewport3D into ElementHost, and call
its ElementHost.DrawToBitmap(), one
caveat here is that you need to call
the DrawToBitmap() at the right time
after the Viewport3D is "fully
rendered", you could manually pump the
messages by calling
System.Windows.Forms.Application.DoEvents(),
and hook up
CompositionTarget.Rendering event to
get callback from the composition
thread (This might work since I am not
exactly sure if the Rendering event is
reliable in this typical
circumstance). BTW, the above method
is based on the assumption that you
don't need to display the ElementHost
on the screen. The ElementHost will be
displayed on the screen, you could
directly call the DrawToBitmap()
method.

Categories

Resources